Merge "Add performance test for operations of recents activity"
diff --git a/Android.bp b/Android.bp
index 7693a66..7687270 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1417,10 +1417,6 @@
     srcs_lib: "framework",
     srcs_lib_whitelist_dirs: frameworks_base_subdirs,
     srcs_lib_whitelist_pkgs: packages_to_document,
-    libs: [
-        "ext",
-        "framework",
-    ],
     local_sourcepaths: frameworks_base_subdirs,
     installable: false,
     annotations_enabled: true,
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index e2ef7a1..2fe0ee7 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -530,10 +530,10 @@
      */
     private void startApp(int userId, String packageName) throws RemoteException {
         final Context context = InstrumentationRegistry.getContext();
-        final WaitResult result = ActivityTaskManager.getService().startActivityAndWait(null, null,
-                context.getPackageManager().getLaunchIntentForPackage(packageName),
-                null, null, null, 0, 0, null, null,
-                userId);
+        final WaitResult result = ActivityTaskManager.getService().startActivityAndWait(null,
+                context.getPackageName(),
+                context.getPackageManager().getLaunchIntentForPackage(packageName), null, null,
+                null, 0, 0, null, null, userId);
         attestTrue("User " + userId + " failed to start " + packageName,
                 result.result == ActivityManager.START_SUCCESS);
     }
diff --git a/api/current.txt b/api/current.txt
index a7b38a5..8636d08 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -103,6 +103,7 @@
     field public static final String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
     field @Deprecated public static final String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
     field @Deprecated public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
+    field public static final String QUERY_ALL_PACKAGES = "android.permission.QUERY_ALL_PACKAGES";
     field public static final String READ_CALENDAR = "android.permission.READ_CALENDAR";
     field public static final String READ_CALL_LOG = "android.permission.READ_CALL_LOG";
     field public static final String READ_CONTACTS = "android.permission.READ_CONTACTS";
@@ -2810,6 +2811,14 @@
     method public void onClicked(android.accessibilityservice.AccessibilityButtonController);
   }
 
+  public final class AccessibilityGestureInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getDisplayId();
+    method public int getGestureId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureInfo> CREATOR;
+  }
+
   public abstract class AccessibilityService extends android.app.Service {
     ctor public AccessibilityService();
     method public final void disableSelf();
@@ -2824,7 +2833,8 @@
     method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
     method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public final android.os.IBinder onBind(android.content.Intent);
-    method protected boolean onGesture(int);
+    method @Deprecated protected boolean onGesture(int);
+    method public boolean onGesture(@NonNull android.accessibilityservice.AccessibilityGestureInfo);
     method public abstract void onInterrupt();
     method protected boolean onKeyEvent(android.view.KeyEvent);
     method protected void onServiceConnected();
@@ -2966,6 +2976,7 @@
   }
 
   public final class GestureDescription {
+    method public int getDisplayId();
     method public static long getMaxGestureDuration();
     method public static int getMaxStrokeCount();
     method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(@IntRange(from=0) int);
@@ -2976,6 +2987,7 @@
     ctor public GestureDescription.Builder();
     method public android.accessibilityservice.GestureDescription.Builder addStroke(@NonNull android.accessibilityservice.GestureDescription.StrokeDescription);
     method public android.accessibilityservice.GestureDescription build();
+    method @NonNull public android.accessibilityservice.GestureDescription.Builder setDisplayId(int);
   }
 
   public static class GestureDescription.StrokeDescription {
@@ -11757,6 +11769,9 @@
     field public static final String FEATURE_SENSOR_RELATIVE_HUMIDITY = "android.hardware.sensor.relative_humidity";
     field public static final String FEATURE_SENSOR_STEP_COUNTER = "android.hardware.sensor.stepcounter";
     field public static final String FEATURE_SENSOR_STEP_DETECTOR = "android.hardware.sensor.stepdetector";
+    field public static final String FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese";
+    field public static final String FEATURE_SE_OMAPI_SD = "android.hardware.se.omapi.sd";
+    field public static final String FEATURE_SE_OMAPI_UICC = "android.hardware.se.omapi.uicc";
     field public static final String FEATURE_SIP = "android.software.sip";
     field public static final String FEATURE_SIP_VOIP = "android.software.sip.voip";
     field public static final String FEATURE_STRONGBOX_KEYSTORE = "android.hardware.strongbox_keystore";
@@ -43991,6 +44006,7 @@
 
   public class CarrierConfigManager {
     method @Nullable public android.os.PersistableBundle getConfig();
+    method @Nullable public android.os.PersistableBundle getConfigByComponentForSubId(String, int);
     method @Nullable public android.os.PersistableBundle getConfigForSubId(int);
     method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle);
     method public void notifyConfigChangedForSubId(int);
@@ -44168,6 +44184,10 @@
     field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
   }
 
+  public static final class CarrierConfigManager.Ims {
+    field public static final String KEY_PREFIX = "ims.";
+  }
+
   public abstract class CellIdentity implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public CharSequence getOperatorAlphaLong();
@@ -44350,6 +44370,7 @@
     method public int getBitErrorRate();
     method public int getDbm();
     method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
+    method public int getRssi();
     method public int getTimingAdvance();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR;
diff --git a/api/test-current.txt b/api/test-current.txt
index b393fcc..8e6ff30 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -32,6 +32,14 @@
 
 }
 
+package android.accessibilityservice {
+
+  public final class AccessibilityGestureInfo implements android.os.Parcelable {
+    ctor public AccessibilityGestureInfo(int, int);
+  }
+
+}
+
 package android.animation {
 
   public class ValueAnimator extends android.animation.Animator {
@@ -3028,6 +3036,7 @@
     field public static final String PERSIST_PREFIX = "persist.sys.fflag.override.";
     field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
     field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
+    field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
   }
 
   public class TimeUtils {
diff --git a/cmds/locksettings/TEST_MAPPING b/cmds/locksettings/TEST_MAPPING
new file mode 100644
index 0000000..c1cba5f
--- /dev/null
+++ b/cmds/locksettings/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+    "presubmit": [
+        {
+            "name": "CtsDevicePolicyManagerTestCases",
+            "options": [
+                {
+                    "include-annotation": "com.android.cts.devicepolicy.annotations.LockSettingsTest"
+                },
+                {
+                    "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+                }
+            ]
+        }
+    ]
+}
diff --git a/cmds/statsd/tools/dogfood/Android.bp b/cmds/statsd/tools/dogfood/Android.bp
deleted file mode 100644
index bb494a6..0000000
--- a/cmds/statsd/tools/dogfood/Android.bp
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//
-
-android_app {
-    name: "StatsdDogfood",
-    platform_apis: true,
-
-    srcs: ["src/**/*.java"],
-
-    resource_dirs: ["res"],
-    static_libs: [
-        "platformprotoslite",
-        "statsdprotolite",
-    ],
-
-    privileged: true,
-    dex_preopt: {
-        enabled: false,
-    },
-    certificate: "platform",
-    optimize: {
-        enabled: false,
-    },
-}
diff --git a/cmds/statsd/tools/dogfood/AndroidManifest.xml b/cmds/statsd/tools/dogfood/AndroidManifest.xml
deleted file mode 100644
index 52673fb..0000000
--- a/cmds/statsd/tools/dogfood/AndroidManifest.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.statsd.dogfood"
-    android:sharedUserId="android.uid.system"
-    android:versionCode="1"
-    android:versionName="1.0" >
-
-    <uses-permission android:name="android.permission.DUMP" />
-
-    <application
-        android:allowBackup="true"
-        android:icon="@drawable/ic_launcher"
-        android:label="@string/app_name" >
-        <activity
-            android:name=".MainActivity"
-            android:label="@string/app_name"
-            android:launchMode="singleTop" >
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-
-        <service android:name=".MainActivity$ReceiverIntentService" android:exported="true" />
-    </application>
-</manifest>
diff --git a/cmds/statsd/tools/dogfood/res/drawable-hdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 55621cc..0000000
--- a/cmds/statsd/tools/dogfood/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/dogfood/res/drawable-mdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 11ec206..0000000
--- a/cmds/statsd/tools/dogfood/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/dogfood/res/drawable-xhdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 7c02b78..0000000
--- a/cmds/statsd/tools/dogfood/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/dogfood/res/drawable-xxhdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 915d914..0000000
--- a/cmds/statsd/tools/dogfood/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
deleted file mode 100644
index 784ed40..0000000
--- a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
+++ /dev/null
@@ -1,162 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" >
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical">
-
-        <Button
-            android:id="@+id/push_config"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:background="@android:color/holo_green_light"
-            android:text="@string/push_config"/>
-        <Button
-                android:id="@+id/set_receiver"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:background="@android:color/holo_green_light"
-                android:text="@string/set_receiver"/>
-        <Button
-                android:id="@+id/remove_receiver"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:background="@android:color/holo_green_light"
-                android:text="@string/remove_receiver"/>
-
-        <LinearLayout android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-            <Button android:id="@+id/app_a_wake_lock_acquire1"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/app_a_get_wl1"/>
-            <Button android:id="@+id/app_a_wake_lock_release1"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/app_a_release_wl1"/>
-        </LinearLayout>
-
-        <LinearLayout android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-            <Button android:id="@+id/app_a_wake_lock_acquire2"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/app_a_get_wl2"/>
-            <Button android:id="@+id/app_a_wake_lock_release2"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/app_a_release_wl2"/>
-        </LinearLayout>
-
-        <LinearLayout android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-            <Button android:id="@+id/app_b_wake_lock_acquire1"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/app_b_get_wl1"/>
-            <Button android:id="@+id/app_b_wake_lock_release1"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/app_b_release_wl1"/>
-        </LinearLayout>
-        <LinearLayout android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-            <Button android:id="@+id/app_b_wake_lock_acquire2"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/app_b_get_wl2"/>
-            <Button android:id="@+id/app_b_wake_lock_release2"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/app_b_release_wl2"/>
-        </LinearLayout>
-
-    <LinearLayout android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal">
-        <Button android:id="@+id/plug"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/plug"/>
-
-        <Button android:id="@+id/unplug"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/unplug"/>
-    </LinearLayout>
-
-        <LinearLayout android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-            <Button android:id="@+id/screen_on"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/screen_on"/>
-
-            <Button android:id="@+id/screen_off"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/screen_off"/>
-        </LinearLayout>
-
-        <LinearLayout android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-
-            <Button
-                android:id="@+id/custom_start"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/custom_start" />
-
-            <Button
-                android:id="@+id/custom_stop"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/custom_stop" />
-        </LinearLayout>
-
-        <Button android:id="@+id/dump"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:background="@android:color/holo_purple"
-            android:text="@string/dump"/>
-
-        <TextView
-            android:id="@+id/header"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:text="@string/report_header"/>
-
-        <TextView
-            android:id="@+id/report_text"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
-
-    </LinearLayout>
-
-</ScrollView>
\ No newline at end of file
diff --git a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config b/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config
deleted file mode 100644
index d050061..0000000
--- a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/dogfood/res/values/strings.xml b/cmds/statsd/tools/dogfood/res/values/strings.xml
deleted file mode 100644
index 60948a1..0000000
--- a/cmds/statsd/tools/dogfood/res/values/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
-
-    <string name="app_name">Statsd Dogfood</string>
-
-    <string name="statsd_running">Statsd Running</string>
-    <string name="statsd_not_running">Statsd NOT Running</string>
-
-    <string name="push_config">Push baseline config</string>
-    <string name="set_receiver">Set pendingintent</string>
-    <string name="remove_receiver">Remove pendingintent</string>
-
-    <string name="app_a_foreground">App A foreground</string>
-    <string name="app_b_foreground">App B foreground</string>
-
-
-    <string name="app_a_get_wl1">App A get wl_1</string>
-    <string name="app_a_release_wl1">App A release wl_1</string>
-
-    <string name="app_a_get_wl2">App A get wl_2</string>
-    <string name="app_a_release_wl2">App A release wl_2</string>
-
-    <string name="app_b_get_wl1">App B get wl_1</string>
-    <string name="app_b_release_wl1">App B release wl_1</string>
-
-    <string name="app_b_get_wl2">App B get wl_2</string>
-    <string name="app_b_release_wl2">App B release wl_2</string>
-
-    <string name="plug">Plug</string>
-    <string name="unplug">Unplug</string>
-
-    <string name="screen_on">Screen On</string>
-    <string name="screen_off">Screen Off</string>
-
-    <string name="custom_start">App hook start</string>
-    <string name="custom_stop">App hook stop</string>
-
-    <string name="dump">DumpReport</string>
-    <string name="report_header">Report details</string>
-</resources>
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
deleted file mode 100644
index b6b16e4..0000000
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.dogfood;
-
-import android.text.format.DateFormat;
-
-import com.android.os.StatsLog;
-
-import java.util.List;
-
-public class DisplayProtoUtils {
-    public static void displayLogReport(StringBuilder sb, StatsLog.ConfigMetricsReportList reports) {
-        sb.append("ConfigKey: ");
-        if (reports.hasConfigKey()) {
-            com.android.os.StatsLog.ConfigMetricsReportList.ConfigKey key = reports.getConfigKey();
-            sb.append("\tuid: ").append(key.getUid()).append(" name: ").append(key.getId())
-                    .append("\n");
-        }
-
-        for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) {
-            sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n");
-            sb.append("Last report time:").append(getDateStr(report.getLastReportElapsedNanos())).
-                    append("\n");
-            sb.append("Current report time:").append(getDateStr(report.getCurrentReportElapsedNanos())).
-                    append("\n");
-            for (StatsLog.StatsLogReport log : report.getMetricsList()) {
-                sb.append("\n\n");
-                sb.append("metric id: ").append(log.getMetricId()).append("\n");
-
-                switch (log.getDataCase()) {
-                    case DURATION_METRICS:
-                        sb.append("Duration metric data\n");
-                        displayDurationMetricData(sb, log);
-                        break;
-                    case EVENT_METRICS:
-                        sb.append("Event metric data\n");
-                        displayEventMetricData(sb, log);
-                        break;
-                    case COUNT_METRICS:
-                        sb.append("Count metric data\n");
-                        displayCountMetricData(sb, log);
-                        break;
-                    case GAUGE_METRICS:
-                        sb.append("Gauge metric data\n");
-                        displayGaugeMetricData(sb, log);
-                        break;
-                    case VALUE_METRICS:
-                        sb.append("Value metric data\n");
-                        displayValueMetricData(sb, log);
-                        break;
-                    case DATA_NOT_SET:
-                        sb.append("No metric data\n");
-                        break;
-                }
-            }
-        }
-    }
-
-    public static String getDateStr(long nanoSec) {
-        return DateFormat.format("dd/MM hh:mm:ss", nanoSec/1000000).toString();
-    }
-
-    private static void displayDimension(StringBuilder sb, StatsLog.DimensionsValue dimensionValue) {
-        sb.append(dimensionValue.getField()).append(":");
-        if (dimensionValue.hasValueBool()) {
-            sb.append(dimensionValue.getValueBool());
-        } else if (dimensionValue.hasValueFloat()) {
-            sb.append(dimensionValue.getValueFloat());
-        } else if (dimensionValue.hasValueInt()) {
-            sb.append(dimensionValue.getValueInt());
-        } else if (dimensionValue.hasValueStr()) {
-            sb.append(dimensionValue.getValueStr());
-        } else if (dimensionValue.hasValueTuple()) {
-            sb.append("{");
-            for (StatsLog.DimensionsValue child :
-                    dimensionValue.getValueTuple().getDimensionsValueList()) {
-                displayDimension(sb, child);
-            }
-            sb.append("}");
-        }
-        sb.append(" ");
-    }
-
-    public static void displayDurationMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
-        StatsLog.StatsLogReport.DurationMetricDataWrapper durationMetricDataWrapper
-                = log.getDurationMetrics();
-        sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n");
-        for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) {
-            sb.append("dimension_in_what: ");
-            displayDimension(sb, duration.getDimensionsInWhat());
-            sb.append("\n");
-            if (duration.hasDimensionsInCondition()) {
-                sb.append("dimension_in_condition: ");
-                displayDimension(sb, duration.getDimensionsInCondition());
-                sb.append("\n");
-            }
-
-            for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList())  {
-                sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-")
-                        .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ")
-                        .append(info.getDurationNanos()).append(" ns\n");
-            }
-        }
-    }
-
-    public static void displayEventMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
-        sb.append("Contains ").append(log.getEventMetrics().getDataCount()).append(" events\n");
-        StatsLog.StatsLogReport.EventMetricDataWrapper eventMetricDataWrapper =
-                log.getEventMetrics();
-        for (StatsLog.EventMetricData event : eventMetricDataWrapper.getDataList()) {
-            sb.append(getDateStr(event.getElapsedTimestampNanos())).append(": ");
-            sb.append(event.getAtom().getPushedCase().toString()).append("\n");
-        }
-    }
-
-    public static void displayCountMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
-        StatsLog.StatsLogReport.CountMetricDataWrapper countMetricDataWrapper
-                = log.getCountMetrics();
-        sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n");
-        for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) {
-            sb.append("dimension_in_what: ");
-            displayDimension(sb, count.getDimensionsInWhat());
-            sb.append("\n");
-            if (count.hasDimensionsInCondition()) {
-                sb.append("dimension_in_condition: ");
-                displayDimension(sb, count.getDimensionsInCondition());
-                sb.append("\n");
-            }
-
-            for (StatsLog.CountBucketInfo info : count.getBucketInfoList())  {
-                sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-")
-                        .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ")
-                        .append(info.getCount()).append("\n");
-            }
-        }
-    }
-
-    public static void displayGaugeMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
-        sb.append("Display me!");
-    }
-
-    public static void displayValueMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
-        sb.append("Display me!");
-    }
-}
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
deleted file mode 100644
index 4f4dd01..0000000
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.dogfood;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.app.IntentService;
-import android.app.StatsManager;
-import android.app.StatsManager.StatsUnavailableException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.util.Log;
-import android.util.StatsLog;
-import android.view.View;
-import android.widget.TextView;
-import android.widget.Toast;
-import android.os.IStatsManager;
-import android.os.ServiceManager;
-
-import java.io.InputStream;
-
-import static com.android.statsd.dogfood.DisplayProtoUtils.displayLogReport;
-
-public class MainActivity extends Activity {
-    private final static String TAG = "StatsdDogfood";
-    private final static long CONFIG_ID = 987654321;
-
-    final int[] mUids = {11111111, 2222222};
-    StatsManager mStatsManager;
-    TextView mReportText;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        setContentView(R.layout.activity_main);
-
-        findViewById(R.id.app_a_wake_lock_acquire1).setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View view) {
-                        onWakeLockAcquire(0, "wl_1");
-                    }
-                });
-
-        findViewById(R.id.app_b_wake_lock_acquire1).setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View view) {
-                        onWakeLockAcquire(1, "wl_1");
-                    }
-                });
-
-        findViewById(R.id.app_a_wake_lock_acquire2).setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View view) {
-                        onWakeLockAcquire(0, "wl_2");
-                    }
-                });
-
-        findViewById(R.id.app_b_wake_lock_acquire2).setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View view) {
-                        onWakeLockAcquire(1, "wl_2");
-                    }
-                });
-
-        findViewById(R.id.app_a_wake_lock_release1).setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View view) {
-                        onWakeLockRelease(0, "wl_1");
-                    }
-                });
-
-
-        findViewById(R.id.app_b_wake_lock_release1).setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View view) {
-                        onWakeLockRelease(1, "wl_1");
-                    }
-                });
-
-        findViewById(R.id.app_a_wake_lock_release2).setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View view) {
-                        onWakeLockRelease(0, "wl_2");
-                    }
-                });
-
-
-        findViewById(R.id.app_b_wake_lock_release2).setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View view) {
-                        onWakeLockRelease(1, "wl_2");
-                    }
-                });
-
-
-        findViewById(R.id.plug).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
-                        StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_AC);
-            }
-        });
-
-        findViewById(R.id.unplug).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
-                        StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_NONE);
-            }
-        });
-
-        findViewById(R.id.screen_on).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
-                        StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_ON);
-            }
-        });
-
-        findViewById(R.id.screen_off).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
-                        StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF);
-            }
-        });
-
-        findViewById(R.id.custom_start).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                StatsLog.logStart(8);
-            }
-        });
-
-        findViewById(R.id.custom_stop).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                StatsLog.logStop(8);
-            }
-        });
-
-        mReportText = (TextView) findViewById(R.id.report_text);
-
-        findViewById(R.id.dump).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                if (!statsdRunning()) {
-                    return;
-                }
-                if (mStatsManager != null) {
-                    try {
-                        byte[] data = mStatsManager.getReports(CONFIG_ID);
-                        if (data != null) {
-                            displayData(data);
-                            return;
-                        }
-                    } catch (StatsUnavailableException e) {
-                        Log.e(TAG, "Failed to get data from statsd", e);
-                    }
-                    mReportText.setText("Failed!");
-                }
-            }
-        });
-
-        findViewById(R.id.push_config).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                try {
-                    if (!statsdRunning()) {
-                        return;
-                    }
-                    Resources res = getResources();
-                    InputStream inputStream = res.openRawResource(R.raw.statsd_baseline_config);
-
-                    byte[] config = new byte[inputStream.available()];
-                    inputStream.read(config);
-                    if (mStatsManager != null) {
-                        try {
-                            mStatsManager.addConfig(CONFIG_ID, config);
-                            Toast.makeText(
-                                    MainActivity.this, "Config pushed", Toast.LENGTH_LONG).show();
-                        } catch (StatsUnavailableException | IllegalArgumentException e) {
-                            Toast.makeText(MainActivity.this, "Config push FAILED!",
-                                    Toast.LENGTH_LONG).show();
-                        }
-                    }
-                } catch (Exception e) {
-                    Toast.makeText(MainActivity.this, "failed to read config", Toast.LENGTH_LONG);
-                }
-            }
-        });
-
-        PendingIntent pi = PendingIntent.getService(this, 0,
-                new Intent(this, ReceiverIntentService.class), PendingIntent.FLAG_UPDATE_CURRENT);
-        findViewById(R.id.set_receiver).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                try {
-                    if (!statsdRunning()) {
-                        return;
-                    }
-                    if (mStatsManager != null) {
-                        try {
-                            mStatsManager.setFetchReportsOperation(pi, CONFIG_ID);
-                            Toast.makeText(MainActivity.this,
-                                    "Receiver specified to pending intent", Toast.LENGTH_LONG)
-                                    .show();
-                        } catch (StatsUnavailableException e) {
-                            Toast.makeText(MainActivity.this, "Statsd did not set receiver",
-                                    Toast.LENGTH_LONG)
-                                    .show();
-                        }
-                    }
-                } catch (Exception e) {
-                    Toast.makeText(MainActivity.this, "failed to set receiver", Toast.LENGTH_LONG);
-                }
-            }
-        });
-        findViewById(R.id.remove_receiver).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                try {
-                    if (!statsdRunning()) {
-                        return;
-                    }
-                    if (mStatsManager != null) {
-                        try {
-                            mStatsManager.setFetchReportsOperation(null, CONFIG_ID);
-                            Toast.makeText(MainActivity.this, "Receiver remove", Toast.LENGTH_LONG)
-                                    .show();
-                        } catch (StatsUnavailableException e) {
-                            Toast.makeText(MainActivity.this, "Statsd did not remove receiver",
-                                    Toast.LENGTH_LONG)
-                                    .show();
-                        }
-                    }
-                } catch (Exception e) {
-                    Toast.makeText(
-                            MainActivity.this, "failed to remove receiver", Toast.LENGTH_LONG);
-                }
-            }
-        });
-        mStatsManager = (StatsManager) getSystemService("stats");
-    }
-
-    private boolean statsdRunning() {
-        if (IStatsManager.Stub.asInterface(ServiceManager.getService("stats")) == null) {
-            Log.d(TAG, "Statsd not running");
-            Toast.makeText(MainActivity.this, "Statsd NOT running!", Toast.LENGTH_LONG).show();
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public void onNewIntent(Intent intent) {
-        Log.d(TAG, "new intent: " + intent.getIntExtra("pkg", 0));
-        int pkg = intent.getIntExtra("pkg", 0);
-        String name = intent.getStringExtra("name");
-        if (intent.hasExtra("acquire")) {
-            onWakeLockAcquire(pkg, name);
-        } else if (intent.hasExtra("release")) {
-            onWakeLockRelease(pkg, name);
-        }
-    }
-
-    private void displayData(byte[] data) {
-        com.android.os.StatsLog.ConfigMetricsReportList reports = null;
-        boolean good = false;
-        if (data != null) {
-            try {
-                reports = com.android.os.StatsLog.ConfigMetricsReportList.parseFrom(data);
-                good = true;
-            } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-                // display it in the text view.
-            }
-        }
-        int size = data == null ? 0 : data.length;
-        StringBuilder sb = new StringBuilder();
-        sb.append(good ? "Proto parsing OK!" : "Proto parsing Error!");
-        sb.append(" size:").append(size).append("\n");
-
-        if (good && reports != null) {
-            displayLogReport(sb, reports);
-            mReportText.setText(sb.toString());
-        }
-    }
-
-
-    private void onWakeLockAcquire(int id, String name) {
-        if (id > 1) {
-            Log.d(TAG, "invalid pkg id");
-            return;
-        }
-        int[] uids = new int[]{mUids[id]};
-        String[] tags = new String[]{"acquire"};
-        StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
-                StatsLog.WAKELOCK_STATE_CHANGED__TYPE__PARTIAL_WAKE_LOCK, name,
-                StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
-        StringBuilder sb = new StringBuilder();
-        sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
-                .append(", ").append(name).append(", 1);");
-        Toast.makeText(this, sb.toString(), Toast.LENGTH_LONG).show();
-    }
-
-    private void onWakeLockRelease(int id, String name) {
-        if (id > 1) {
-            Log.d(TAG, "invalid pkg id");
-            return;
-        }
-        int[] uids = new int[]{mUids[id]};
-        String[] tags = new String[]{"release"};
-        StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
-                StatsLog.WAKELOCK_STATE_CHANGED__TYPE__PARTIAL_WAKE_LOCK, name,
-                StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
-        StringBuilder sb = new StringBuilder();
-        sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
-                .append(", ").append(name).append(", 0);");
-        Toast.makeText(this, sb.toString(), Toast.LENGTH_LONG).show();
-    }
-
-    public static class ReceiverIntentService extends IntentService {
-        public ReceiverIntentService() {
-            super("ReceiverIntentService");
-        }
-
-        /**
-         * The IntentService calls this method from the default worker thread with
-         * the intent that started the service. When this method returns, IntentService
-         * stops the service, as appropriate.
-         */
-        @Override
-        protected void onHandleIntent(Intent intent) {
-            Log.i(TAG, "Received notification that we should call getData");
-        }
-    }
-}
diff --git a/cmds/statsd/tools/loadtest/Android.bp b/cmds/statsd/tools/loadtest/Android.bp
deleted file mode 100644
index bf87fc5..0000000
--- a/cmds/statsd/tools/loadtest/Android.bp
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//
-
-android_app {
-    name: "StatsdLoadtest",
-    platform_apis: true,
-
-    srcs: ["src/**/*.java"],
-
-    resource_dirs: ["res"],
-    static_libs: [
-        "platformprotoslite",
-        "statsdprotolite",
-    ],
-
-    certificate: "platform",
-    privileged: true,
-    dex_preopt: {
-        enabled: false,
-    },
-    optimize: {
-        enabled: false,
-    },
-}
diff --git a/cmds/statsd/tools/loadtest/AndroidManifest.xml b/cmds/statsd/tools/loadtest/AndroidManifest.xml
deleted file mode 100644
index 2bf8ca9..0000000
--- a/cmds/statsd/tools/loadtest/AndroidManifest.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.statsd.loadtest"
-    android:sharedUserId="android.uid.system"
-    android:versionCode="1"
-    android:versionName="1.0" >
-
-  <uses-permission android:name="android.permission.DUMP" />
-  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-  <application
-      android:allowBackup="true"
-      android:icon="@drawable/ic_launcher"
-      android:label="@string/app_name" >
-    <activity
-        android:name=".LoadtestActivity"
-        android:label="@string/app_name"
-        android:launchMode="singleTop" >
-      <intent-filter>
-        <action android:name="android.intent.action.MAIN" />
-        <category android:name="android.intent.category.LAUNCHER" />
-      </intent-filter>
-    </activity>
-    <receiver android:name=".LoadtestActivity$PusherAlarmReceiver" />
-    <receiver android:name=".LoadtestActivity$StopperAlarmReceiver"/>
-    <receiver android:name=".PerfData$PerfAlarmReceiver"/>
-  </application>
-</manifest>
diff --git a/cmds/statsd/tools/loadtest/res/drawable-hdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 55621cc..0000000
--- a/cmds/statsd/tools/loadtest/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 11ec206..0000000
--- a/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 7c02b78..0000000
--- a/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 915d914..0000000
--- a/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
deleted file mode 100644
index d6f8047..0000000
--- a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
+++ /dev/null
@@ -1,208 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" >
-
-    <LinearLayout
-        android:id="@+id/outside"
-        android:clickable="true"
-        android:focusable="true"
-        android:focusableInTouchMode="true"
-        android:gravity="center"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginRight="10dp"
-        android:layout_marginLeft="10dp"
-        android:orientation="vertical">
-      <requestFocus />
-
-        <LinearLayout
-            android:gravity="center"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:orientation="horizontal">
-            <TextView
-                android:textSize="30dp"
-                android:layout_height="wrap_content"
-                android:layout_width="wrap_content"
-                android:text="@string/replication_label" />
-            <EditText
-                android:id="@+id/replication"
-                android:inputType="number"
-                android:layout_weight="1"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:maxLength="4"
-                android:text="@integer/replication_default"
-                android:textSize="30dp"/>
-        </LinearLayout>
-
-        <LinearLayout
-            android:gravity="center"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:orientation="horizontal">
-            <TextView
-                android:textSize="30dp"
-                android:layout_height="wrap_content"
-                android:layout_width="wrap_content"
-                android:text="@string/bucket_label" />
-             <Spinner
-                 android:id="@+id/bucket_spinner"
-                 android:layout_width="wrap_content"
-                 android:layout_height="wrap_content"
-                 android:prompt="@string/bucket_label"/>
-        </LinearLayout>
-
-        <LinearLayout
-            android:gravity="center"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:orientation="horizontal">
-            <TextView
-                android:textSize="30dp"
-                android:layout_height="wrap_content"
-                android:layout_width="wrap_content"
-                android:text="@string/period_label" />
-            <EditText
-                android:id="@+id/period"
-                android:inputType="number"
-                android:layout_weight="1"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:maxLength="3"
-                android:text="@integer/period_default"
-                android:textSize="30dp"/>
-        </LinearLayout>
-
-        <LinearLayout
-            android:gravity="center"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:orientation="horizontal">
-            <TextView
-                android:textSize="30dp"
-                android:layout_height="wrap_content"
-                android:layout_width="wrap_content"
-                android:text="@string/burst_label" />
-            <EditText
-                android:id="@+id/burst"
-                android:inputType="number"
-                android:layout_weight="1"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:maxLength="4"
-                android:text="@integer/burst_default"
-                android:textSize="30dp"/>
-        </LinearLayout>
-
-        <LinearLayout
-            android:gravity="center"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:orientation="horizontal">
-            <TextView
-                android:textSize="30dp"
-                android:layout_height="wrap_content"
-                android:layout_width="wrap_content"
-                android:text="@string/duration_label" />
-            <EditText
-                android:id="@+id/duration"
-                android:inputType="number"
-                android:layout_weight="1"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:maxLength="4"
-                android:text="@integer/duration_default"
-                android:textSize="30dp"/>
-        </LinearLayout>
-        <CheckBox
-            android:id="@+id/placebo"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/placebo"
-            android:checked="false" />
-
-        <LinearLayout
-            android:gravity="center"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-            <CheckBox
-                android:id="@+id/include_count"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/count"
-                android:checked="true"/>
-            <CheckBox
-                android:id="@+id/include_duration"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/duration"
-                android:checked="true"/>
-            <CheckBox
-                android:id="@+id/include_event"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/event"
-                android:checked="true"/>
-            <CheckBox
-                android:id="@+id/include_value"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/value"
-                android:checked="true"/>
-            <CheckBox
-                android:id="@+id/include_gauge"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/gauge"
-                android:checked="true"/>
-        </LinearLayout>
-
-        <Space
-            android:layout_width="1dp"
-            android:layout_height="30dp"/>
-
-        <Button
-            android:id="@+id/start_stop"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:background="#ffff0000"
-            android:text="@string/start"
-            android:textSize="50dp"/>
-
-        <Space
-            android:layout_width="1dp"
-            android:layout_height="30dp"/>
-
-        <Space
-            android:layout_width="1dp"
-            android:layout_height="30dp"/>
-
-        <TextView
-            android:id="@+id/report_text"
-            android:gravity="center"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
-
-    </LinearLayout>
-
-</ScrollView>
diff --git a/cmds/statsd/tools/loadtest/res/layout/spinner_item.xml b/cmds/statsd/tools/loadtest/res/layout/spinner_item.xml
deleted file mode 100644
index b03da06..0000000
--- a/cmds/statsd/tools/loadtest/res/layout/spinner_item.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<TextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:textSize="30dp"
-    android:gravity="left"
-    android:padding="5dip"
-    />
diff --git a/cmds/statsd/tools/loadtest/res/raw/loadtest_config b/cmds/statsd/tools/loadtest/res/raw/loadtest_config
deleted file mode 100755
index 2422190..0000000
--- a/cmds/statsd/tools/loadtest/res/raw/loadtest_config
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/values/integers.xml b/cmds/statsd/tools/loadtest/res/values/integers.xml
deleted file mode 100644
index c2407d3..0000000
--- a/cmds/statsd/tools/loadtest/res/values/integers.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
-    <integer name="burst_default">1</integer>
-    <integer name="period_default">2</integer>
-    <integer name="replication_default">1</integer>
-    <integer name="duration_default">240</integer>
-</resources>
diff --git a/cmds/statsd/tools/loadtest/res/values/strings.xml b/cmds/statsd/tools/loadtest/res/values/strings.xml
deleted file mode 100644
index e8ae3f8..0000000
--- a/cmds/statsd/tools/loadtest/res/values/strings.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
-    <string name="app_name">Statsd Loadtest</string>
-    <string name="bucket_label">bucket size (mins):&#160;</string>
-    <string name="burst_label">burst:&#160;</string>
-    <string name="bucket_default">FIVE_MINUTES</string>
-    <string name="placebo">placebo</string>
-    <string name="period_label">logging period (secs):&#160;</string>
-    <string name="replication_label">metric replication:&#160;</string>
-    <string name="duration_label">test duration (mins):&#160;</string>
-    <string name="start"> &#160;Start&#160; </string>
-    <string name="stop"> &#160;Stop&#160; </string>
-    <string name="count"> count </string>
-    <string name="duration"> duration </string>
-    <string name="event"> event </string>
-    <string name="value"> value </string>
-    <string name="gauge"> gauge </string>
-
-</resources>
diff --git a/cmds/statsd/tools/loadtest/run_loadtest.sh b/cmds/statsd/tools/loadtest/run_loadtest.sh
deleted file mode 100755
index 3c93a06..0000000
--- a/cmds/statsd/tools/loadtest/run_loadtest.sh
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/bin/sh
-#
-# Script that measures statsd's PSS under an increasing number of metrics.
-
-# Globals.
-pss=""
-pid=""
-
-# Starts the loadtest.
-start_loadtest() {
-    echo "Starting loadtest"
-    adb shell am start -n com.android.statsd.loadtest/.LoadtestActivity --es "type" "start"
-}
-
-# Stops the loadtest.
-stop_loadtest() {
-    echo "Stopping loadtest"
-    adb shell am start -n com.android.statsd.loadtest/.LoadtestActivity --es "type" "stop"
-}
-
-# Sets the metrics replication.
-# Arguments:
-#   $1: The replication factor.
-set_replication() {
-    adb shell am start -n com.android.statsd.loadtest/.LoadtestActivity --es "type" "set_replication" --ei "replication" "${1}"
-    echo "Replication set to ${1}"
-}
-
-# Reads statsd's pid and PSS.
-update_pid_and_pss() {
-    # Command that reads the PSS for statsd. This also gives us its pid.
-    get_mem=$(adb shell dumpsys meminfo |grep statsd)
-    # Looks for statsd's pid.
-    regex="([0-9,]+)K: statsd \(pid ([0-9]+)\).*"
-    if [[ $get_mem =~ $regex ]]; then
-        pss=$(echo "${BASH_REMATCH[1]}" | tr -d , | sed 's/\.//g')
-        pid=$(echo "${BASH_REMATCH[2]}")
-    else
-        echo $cmd doesnt match $regex
-    fi
-}
-
-# Kills statsd.
-# Assumes the pid has been set.
-kill_statsd() {
-    echo "Killing statsd (pid ${pid})"
-    adb shell kill -9 "${pid}"
-}
-
-# Main loop.
-main() {
-    start_time=$(date +%s)
-    values=()
-    stop_loadtest
-
-    echo ""
-    echo "********************* NEW LOADTEST ************************"
-    update_pid_and_pss
-    for replication in 1 2 4 8 16 32 64 128 256 512 1024 2048 4096
-    do
-        echo "**** Starting test at replication ${replication} ****"
-
-        # (1) Restart statsd. This will ensure its state is empty.
-        kill_statsd
-        sleep 3 # wait a bit for it to restart
-        update_pid_and_pss
-        echo "Before the test, statsd's PSS is ${pss}"
-
-        # (2) Set the replication.
-        set_replication "${replication}"
-        sleep 1 # wait a bit
-
-        # (3) Start the loadtest.
-        start_loadtest
-
-        # (4) Wait several seconds, then read the PSS.
-        sleep 100 && update_pid_and_pss
-        echo "During the test, statsd's PSS is ${pss}"
-        values+=(${pss})
-
-        echo "Values: ${values[@]}"
-
-        # (5) Stop loadtest.
-        stop_loadtest
-        sleep 2
-
-        echo ""
-    done
-
-    end_time=$(date +%s)
-    echo "Completed loadtest in $((${end_time} - ${start_time})) seconds."
-
-    values_as_str=$(IFS=$'\n'; echo "${values[*]}")
-    echo "The PSS values are:"
-    echo "${values_as_str}"
-    echo ""
-}
-
-main
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java
deleted file mode 100644
index bab0c1e..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.loadtest;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.util.Log;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import java.text.ParseException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class BatteryDataRecorder extends PerfDataRecorder {
-    private static final String TAG = "loadtest.BatteryDataRecorder";
-    private static final String DUMP_FILENAME = TAG + "_dump.tmp";
-
-    public BatteryDataRecorder(boolean placebo, int replication, TimeUnit bucket, long periodSecs,
-        int burst, boolean includeCountMetric, boolean includeDurationMetric,
-        boolean includeEventMetric,  boolean includeValueMetric, boolean includeGaugeMetric) {
-      super(placebo, replication, bucket, periodSecs, burst, includeCountMetric,
-          includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric);
-    }
-
-    @Override
-    public void startRecording(Context context) {
-        // Reset batterystats.
-        runDumpsysStats(context, DUMP_FILENAME, "batterystats", "--reset");
-    }
-
-    @Override
-    public void onAlarm(Context context) {
-        // Nothing to do as for battery, the whole data is in the final dumpsys call.
-    }
-
-    @Override
-    public void stopRecording(Context context) {
-        StringBuilder sb = new StringBuilder();
-        // Don't use --checkin.
-        runDumpsysStats(context, DUMP_FILENAME, "batterystats");
-        readDumpData(context, DUMP_FILENAME, new BatteryStatsParser(), sb);
-        writeData(context, "battery_", "time,battery_level", sb);
-    }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java
deleted file mode 100644
index 203d97a..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.loadtest;
-
-import android.annotation.Nullable;
-import android.util.Log;
-import java.text.ParseException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class BatteryStatsParser implements PerfParser {
-
-    private static final Pattern LINE_PATTERN =
-        Pattern.compile("\\s*\\+*(\\S*)\\s\\(\\d+\\)\\s(\\d\\d\\d)\\s.*");
-    private static final Pattern TIME_PATTERN =
-        Pattern.compile("(\\d+)?(h)?(\\d+)?(m)?(\\d+)?(s)?(\\d+)?(ms)?");
-    private static final String TAG = "loadtest.BatteryStatsParser";
-
-    private boolean mHistoryStarted;
-    private boolean mHistoryEnded;
-
-    public BatteryStatsParser() {
-    }
-
-    @Override
-    @Nullable
-    public String parseLine(String line) {
-        if (mHistoryEnded) {
-            return null;
-        }
-        if (!mHistoryStarted) {
-            if (line.contains("Battery History")) {
-                mHistoryStarted = true;
-            }
-            return null;
-        }
-        if (line.isEmpty()) {
-            mHistoryEnded = true;
-            return null;
-        }
-        Matcher lineMatcher = LINE_PATTERN.matcher(line);
-        if (lineMatcher.find() && lineMatcher.group(1) != null && lineMatcher.group(2) != null) {
-            if (lineMatcher.group(1).equals("0")) {
-                return "0," + lineMatcher.group(2) + "\n";
-            } else {
-                Matcher timeMatcher = TIME_PATTERN.matcher(lineMatcher.group(1));
-                if (timeMatcher.find()) {
-                    Long time = getTime(lineMatcher.group(1));
-                    if (time != null) {
-                        return time + "," + lineMatcher.group(2) + "\n";
-                      } else {
-                        return null; // bad time
-                    }
-                } else {
-                  return null;  // bad or no time
-                }
-            }
-        }
-        return null;
-    }
-
-    @Nullable
-    private Long getTime(String group) {
-        if ("0".equals(group)) {
-            return 0L;
-        }
-        Matcher timeMatcher = TIME_PATTERN.matcher(group);
-        if (!timeMatcher.find()) {
-            return null;
-        }
-
-        // Get rid of "ms".
-        String[] matches = group.split("ms", -1);
-        if (matches.length > 1) {
-            group = matches[0];
-        }
-
-        long time = 0L;
-        matches = group.split("h");
-        if (matches.length > 1) {
-            time += Long.parseLong(matches[0]) * 60 * 60 * 1000;  // hours
-            group = matches[1];
-        }
-        matches = group.split("m");
-        if (matches.length > 1) {
-            time += Long.parseLong(matches[0]) * 60 * 1000;  // minutes
-            group = matches[1];
-        }
-        matches = group.split("s");
-        if (matches.length > 1) {
-            time += Long.parseLong(matches[0]) * 1000; // seconds
-            group = matches[1];
-        }
-
-        if (!group.isEmpty()) {
-            time += Long.parseLong(group); // milliseconds
-        }
-        return time;
-    }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
deleted file mode 100644
index 2e0161b..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.loadtest;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-
-import com.android.internal.os.StatsdConfigProto.Predicate;
-import com.android.internal.os.StatsdConfigProto.CountMetric;
-import com.android.internal.os.StatsdConfigProto.DurationMetric;
-import com.android.internal.os.StatsdConfigProto.MetricConditionLink;
-import com.android.internal.os.StatsdConfigProto.EventMetric;
-import com.android.internal.os.StatsdConfigProto.GaugeMetric;
-import com.android.internal.os.StatsdConfigProto.ValueMetric;
-import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
-import com.android.internal.os.StatsdConfigProto.AtomMatcher;
-import com.android.internal.os.StatsdConfigProto.SimplePredicate;
-import com.android.internal.os.StatsdConfigProto.StatsdConfig;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-
-import java.io.InputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Creates StatsdConfig protos for loadtesting.
- */
-public class ConfigFactory {
-    public static class ConfigMetadata {
-        public final byte[] bytes;
-        public final int numMetrics;
-
-        public ConfigMetadata(byte[] bytes, int numMetrics) {
-            this.bytes = bytes;
-            this.numMetrics = numMetrics;
-        }
-    }
-
-    public static final long CONFIG_ID = 123456789;
-
-    private static final String TAG = "loadtest.ConfigFactory";
-
-    private final StatsdConfig mTemplate;
-
-    public ConfigFactory(Context context) {
-        // Read the config template from the resoures.
-        Resources res = context.getResources();
-        byte[] template = null;
-        StatsdConfig templateProto = null;
-        try {
-            InputStream inputStream = res.openRawResource(R.raw.loadtest_config);
-            template = new byte[inputStream.available()];
-            inputStream.read(template);
-            templateProto = StatsdConfig.parseFrom(template);
-        } catch (IOException e) {
-            Log.e(TAG, "Unable to read or parse loadtest config template. Using an empty config.");
-        }
-        mTemplate = templateProto == null ? StatsdConfig.newBuilder().build() : templateProto;
-
-        Log.d(TAG, "Loadtest template config: " + mTemplate);
-    }
-
-    /**
-     * Generates a config.
-     *
-     * All configs are based on the same template.
-     * That template is designed to make the most use of the set of atoms that {@code SequencePusher}
-     * pushes, and to exercise as many of the metrics features as possible.
-     * Furthermore, by passing a replication factor to this method, one can artificially inflate
-     * the number of metrics in the config. One can also adjust the bucket size for aggregate
-     * metrics.
-     *
-     * @param replication The number of times each metric is replicated in the config.
-     *        If the config template has n metrics, the generated config will have n * replication
-     *        ones
-     * @param bucketMillis The bucket size, in milliseconds, for aggregate metrics
-     * @param placebo If true, only return an empty config
-     * @return The serialized config and the number of metrics.
-     */
-    public ConfigMetadata getConfig(int replication, TimeUnit bucket, boolean placebo,
-            boolean includeCount, boolean includeDuration, boolean includeEvent,
-            boolean includeValue, boolean includeGauge) {
-        StatsdConfig.Builder config = StatsdConfig.newBuilder()
-            .setId(CONFIG_ID);
-        if (placebo) {
-          replication = 0;  // Config will be empty, aside from a name.
-        }
-        int numMetrics = 0;
-        for (int i = 0; i < replication; i++) {
-            // metrics
-            if (includeEvent) {
-                for (EventMetric metric : mTemplate.getEventMetricList()) {
-                    addEventMetric(metric, i, config);
-                    numMetrics++;
-                }
-            }
-            if (includeCount) {
-                for (CountMetric metric : mTemplate.getCountMetricList()) {
-                    addCountMetric(metric, i, bucket, config);
-                    numMetrics++;
-                }
-            }
-            if (includeDuration) {
-                for (DurationMetric metric : mTemplate.getDurationMetricList()) {
-                    addDurationMetric(metric, i, bucket, config);
-                    numMetrics++;
-                }
-            }
-            if (includeGauge) {
-                for (GaugeMetric metric : mTemplate.getGaugeMetricList()) {
-                    addGaugeMetric(metric, i, bucket, config);
-                    numMetrics++;
-                }
-            }
-            if (includeValue) {
-                for (ValueMetric metric : mTemplate.getValueMetricList()) {
-                    addValueMetric(metric, i, bucket, config);
-                    numMetrics++;
-                }
-            }
-            // predicates
-            for (Predicate predicate : mTemplate.getPredicateList()) {
-              addPredicate(predicate, i, config);
-            }
-            // matchers
-            for (AtomMatcher matcher : mTemplate.getAtomMatcherList()) {
-              addMatcher(matcher, i, config);
-            }
-        }
-
-        Log.d(TAG, "Loadtest config is : " + config.build());
-        Log.d(TAG, "Generated config has " + numMetrics + " metrics");
-
-        return new ConfigMetadata(config.build().toByteArray(), numMetrics);
-    }
-
-    /**
-     * Creates {@link MetricConditionLink}s that are identical to the one passed to this method,
-     * except that the names are appended with the provided suffix.
-     */
-    private List<MetricConditionLink> getLinks(
-        List<MetricConditionLink> links, int suffix) {
-        List<MetricConditionLink> newLinks = new ArrayList();
-        for (MetricConditionLink link : links) {
-            newLinks.add(link.toBuilder()
-                .setCondition(link.getCondition() + suffix)
-                .build());
-        }
-        return newLinks;
-    }
-
-    /**
-     * Creates an {@link EventMetric} based on the template. Makes sure that all names are appended
-     * with the provided suffix. Then adds that metric to the config.
-     */
-    private void addEventMetric(EventMetric template, int suffix, StatsdConfig.Builder config) {
-        EventMetric.Builder metric = template.toBuilder()
-            .setId(template.getId() + suffix)
-            .setWhat(template.getWhat() + suffix);
-        if (template.hasCondition()) {
-            metric.setCondition(template.getCondition() + suffix);
-        }
-        if (template.getLinksCount() > 0) {
-            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
-            metric.clearLinks();
-            metric.addAllLinks(links);
-        }
-        config.addEventMetric(metric);
-    }
-
-    /**
-     * Creates a {@link CountMetric} based on the template. Makes sure that all names are appended
-     * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
-     */
-    private void addCountMetric(CountMetric template, int suffix, TimeUnit bucket,
-        StatsdConfig.Builder config) {
-        CountMetric.Builder metric = template.toBuilder()
-            .setId(template.getId() + suffix)
-            .setWhat(template.getWhat() + suffix);
-        if (template.hasCondition()) {
-            metric.setCondition(template.getCondition() + suffix);
-        }
-        if (template.getLinksCount() > 0) {
-            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
-            metric.clearLinks();
-            metric.addAllLinks(links);
-        }
-        metric.setBucket(bucket);
-        config.addCountMetric(metric);
-    }
-
-    /**
-     * Creates a {@link DurationMetric} based on the template. Makes sure that all names are appended
-     * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
-     */
-    private void addDurationMetric(DurationMetric template, int suffix, TimeUnit bucket,
-        StatsdConfig.Builder config) {
-        DurationMetric.Builder metric = template.toBuilder()
-            .setId(template.getId() + suffix)
-            .setWhat(template.getWhat() + suffix);
-        if (template.hasCondition()) {
-            metric.setCondition(template.getCondition() + suffix);
-        }
-        if (template.getLinksCount() > 0) {
-            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
-            metric.clearLinks();
-            metric.addAllLinks(links);
-        }
-        metric.setBucket(bucket);
-        config.addDurationMetric(metric);
-    }
-
-    /**
-     * Creates a {@link GaugeMetric} based on the template. Makes sure that all names are appended
-     * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
-     */
-    private void addGaugeMetric(GaugeMetric template, int suffix, TimeUnit bucket,
-        StatsdConfig.Builder config) {
-        GaugeMetric.Builder metric = template.toBuilder()
-            .setId(template.getId() + suffix)
-            .setWhat(template.getWhat() + suffix);
-        if (template.hasCondition()) {
-            metric.setCondition(template.getCondition() + suffix);
-        }
-        if (template.getLinksCount() > 0) {
-            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
-            metric.clearLinks();
-            metric.addAllLinks(links);
-        }
-        metric.setBucket(bucket);
-        config.addGaugeMetric(metric);
-    }
-
-    /**
-     * Creates a {@link ValueMetric} based on the template. Makes sure that all names are appended
-     * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
-     */
-    private void addValueMetric(ValueMetric template, int suffix, TimeUnit bucket,
-        StatsdConfig.Builder config) {
-        ValueMetric.Builder metric = template.toBuilder()
-            .setId(template.getId() + suffix)
-            .setWhat(template.getWhat() + suffix);
-        if (template.hasCondition()) {
-            metric.setCondition(template.getCondition() + suffix);
-        }
-        if (template.getLinksCount() > 0) {
-            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
-            metric.clearLinks();
-            metric.addAllLinks(links);
-        }
-        metric.setBucket(bucket);
-        config.addValueMetric(metric);
-    }
-
-    /**
-     * Creates a {@link Predicate} based on the template. Makes sure that all names
-     * are appended with the provided suffix. Then adds that predicate to the config.
-     */
-    private void addPredicate(Predicate template, int suffix, StatsdConfig.Builder config) {
-        Predicate.Builder predicate = template.toBuilder()
-            .setId(template.getId() + suffix);
-        if (template.hasCombination()) {
-            Predicate.Combination.Builder cb = template.getCombination().toBuilder()
-                .clearPredicate();
-            for (long child : template.getCombination().getPredicateList()) {
-                cb.addPredicate(child + suffix);
-            }
-            predicate.setCombination(cb.build());
-        }
-        if (template.hasSimplePredicate()) {
-            SimplePredicate.Builder sc = template.getSimplePredicate().toBuilder()
-                .setStart(template.getSimplePredicate().getStart() + suffix)
-                .setStop(template.getSimplePredicate().getStop() + suffix);
-            if (template.getSimplePredicate().hasStopAll()) {
-                sc.setStopAll(template.getSimplePredicate().getStopAll() + suffix);
-            }
-            predicate.setSimplePredicate(sc.build());
-        }
-        config.addPredicate(predicate);
-    }
-
-    /**
-     * Creates a {@link AtomMatcher} based on the template. Makes sure that all names
-     * are appended with the provided suffix. Then adds that matcher to the config.
-     */
-    private void addMatcher(AtomMatcher template, int suffix, StatsdConfig.Builder config) {
-        AtomMatcher.Builder matcher = template.toBuilder()
-            .setId(template.getId() + suffix);
-        if (template.hasCombination()) {
-            AtomMatcher.Combination.Builder cb = template.getCombination().toBuilder()
-                .clearMatcher();
-            for (long child : template.getCombination().getMatcherList()) {
-                cb.addMatcher(child + suffix);
-            }
-            matcher.setCombination(cb);
-        }
-        config.addAtomMatcher(matcher);
-    }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
deleted file mode 100644
index d55f3f3..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.loadtest;
-
-import android.text.format.DateFormat;
-
-import com.android.os.StatsLog;
-
-import java.util.List;
-
-public class DisplayProtoUtils {
-    private static final int MAX_NUM_METRICS_TO_DISPLAY = 10;
-
-    public static void displayLogReport(StringBuilder sb, StatsLog.ConfigMetricsReportList reports) {
-        sb.append("******************** Report ********************\n");
-        if (reports.hasConfigKey()) {
-            sb.append("ConfigKey: ");
-            com.android.os.StatsLog.ConfigMetricsReportList.ConfigKey key = reports.getConfigKey();
-            sb.append("\tuid: ").append(key.getUid()).append(" id: ").append(key.getId())
-                    .append("\n");
-        }
-
-        int numMetrics = 0;
-        for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) {
-            sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n");
-            sb.append("Last report time:").append(getDateStr(report.getLastReportElapsedNanos())).
-                    append("\n");
-            sb.append("Current report time:").append(getDateStr(report.getCurrentReportElapsedNanos())).
-                    append("\n");
-            for (StatsLog.StatsLogReport log : report.getMetricsList()) {
-                numMetrics++;
-                if (numMetrics > MAX_NUM_METRICS_TO_DISPLAY) {
-                    sb.append("... output truncated\n");
-                    sb.append("************************************************");
-                    return;
-                }
-                sb.append("\n");
-                sb.append("metric id: ").append(log.getMetricId()).append("\n");
-
-                switch (log.getDataCase()) {
-                    case DURATION_METRICS:
-                        sb.append("Duration metric data\n");
-                        displayDurationMetricData(sb, log);
-                        break;
-                    case EVENT_METRICS:
-                        sb.append("Event metric data\n");
-                        displayEventMetricData(sb, log);
-                        break;
-                    case COUNT_METRICS:
-                        sb.append("Count metric data\n");
-                        displayCountMetricData(sb, log);
-                        break;
-                    case GAUGE_METRICS:
-                        sb.append("Gauge metric data\n");
-                        displayGaugeMetricData(sb, log);
-                        break;
-                    case VALUE_METRICS:
-                        sb.append("Value metric data\n");
-                        displayValueMetricData(sb, log);
-                        break;
-                    case DATA_NOT_SET:
-                        sb.append("No metric data\n");
-                        break;
-                }
-            }
-        }
-        sb.append("************************************************");
-    }
-
-    public static String getDateStr(long nanoSec) {
-        return DateFormat.format("dd/MM hh:mm:ss", nanoSec/1000000).toString();
-    }
-
-    private static void displayDimension(StringBuilder sb, StatsLog.DimensionsValue dimensionValue) {
-        sb.append(dimensionValue.getField()).append(":");
-        if (dimensionValue.hasValueBool()) {
-            sb.append(dimensionValue.getValueBool());
-        } else if (dimensionValue.hasValueFloat()) {
-            sb.append(dimensionValue.getValueFloat());
-        } else if (dimensionValue.hasValueInt()) {
-            sb.append(dimensionValue.getValueInt());
-        } else if (dimensionValue.hasValueStr()) {
-            sb.append(dimensionValue.getValueStr());
-        } else if (dimensionValue.hasValueTuple()) {
-            sb.append("{");
-            for (StatsLog.DimensionsValue child :
-                    dimensionValue.getValueTuple().getDimensionsValueList()) {
-                displayDimension(sb, child);
-            }
-            sb.append("}");
-        }
-        sb.append(" ");
-    }
-
-    public static void displayDurationMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
-        StatsLog.StatsLogReport.DurationMetricDataWrapper durationMetricDataWrapper
-                = log.getDurationMetrics();
-        sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n");
-        for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) {
-            sb.append("dimension_in_what: ");
-            displayDimension(sb, duration.getDimensionsInWhat());
-            sb.append("\n");
-            if (duration.hasDimensionsInCondition()) {
-                sb.append("dimension_in_condition: ");
-                displayDimension(sb, duration.getDimensionsInCondition());
-                sb.append("\n");
-            }
-
-            for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList())  {
-                sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-")
-                        .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ")
-                        .append(info.getDurationNanos()).append(" ns\n");
-            }
-        }
-    }
-
-    public static void displayEventMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
-        sb.append("Contains ").append(log.getEventMetrics().getDataCount()).append(" events\n");
-        StatsLog.StatsLogReport.EventMetricDataWrapper eventMetricDataWrapper =
-                log.getEventMetrics();
-        for (StatsLog.EventMetricData event : eventMetricDataWrapper.getDataList()) {
-            sb.append(getDateStr(event.getElapsedTimestampNanos())).append(": ");
-            sb.append(event.getAtom().getPushedCase().toString()).append("\n");
-        }
-    }
-
-    public static void displayCountMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
-        StatsLog.StatsLogReport.CountMetricDataWrapper countMetricDataWrapper
-                = log.getCountMetrics();
-        sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n");
-        for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) {
-            sb.append("dimension_in_what: ");
-            displayDimension(sb, count.getDimensionsInWhat());
-            sb.append("\n");
-            if (count.hasDimensionsInCondition()) {
-                sb.append("dimension_in_condition: ");
-                displayDimension(sb, count.getDimensionsInCondition());
-                sb.append("\n");
-            }
-
-            for (StatsLog.CountBucketInfo info : count.getBucketInfoList())  {
-                sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-")
-                        .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ")
-                        .append(info.getCount()).append("\n");
-            }
-        }
-    }
-
-    public static void displayGaugeMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
-        sb.append("Display me!");
-    }
-
-    public static void displayValueMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
-        sb.append("Display me!");
-    }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
deleted file mode 100644
index 769f78c..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
+++ /dev/null
@@ -1,756 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.loadtest;
-
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.StatsManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IStatsManager;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.util.StatsLog;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-import android.view.MotionEvent;
-import android.view.View.OnFocusChangeListener;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.Spinner;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.os.StatsLog.ConfigMetricsReport;
-import com.android.os.StatsLog.ConfigMetricsReportList;
-import com.android.os.StatsLog.StatsdStatsReport;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Runs a load test for statsd.
- * How it works:
- * <ul>
- * <li> Sets up and pushes a custom config with metrics that exercise a large swath of code paths.
- * <li> Periodically logs certain atoms into logd.
- * <li> Impact on battery can be printed to logcat, or a bug report can be filed and analyzed
- * in battery Historian.
- * </ul>
- * The load depends on how demanding the config is, as well as how frequently atoms are pushsed
- * to logd. Those are all controlled by 4 adjustable parameters:
- * <ul>
- * <li> The 'replication' parameter artificially multiplies the number of metrics in the config.
- * <li> The bucket size controls the time-bucketing the aggregate metrics.
- * <li> The period parameter controls how frequently atoms are pushed to logd.
- * <li> The 'burst' parameter controls how many atoms are pushed at the same time (per period).
- * </ul>
- */
-public class LoadtestActivity extends Activity implements AdapterView.OnItemSelectedListener {
-
-    private static final String TAG = "loadtest.LoadtestActivity";
-    public static final String TYPE = "type";
-    private static final String PUSH_ALARM = "push_alarm";
-    public static final String PERF_ALARM = "perf_alarm";
-    private static final String SET_REPLICATION = "set_replication";
-    private static final String REPLICATION = "replication";
-    private static final String START = "start";
-    private static final String STOP = "stop";
-    private static final Map<String, TimeUnit> TIME_UNIT_MAP = initializeTimeUnitMap();
-    private static final List<String> TIME_UNIT_LABELS = initializeTimeUnitLabels();
-
-    public final static class PusherAlarmReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Intent activityIntent = new Intent(context, LoadtestActivity.class);
-            activityIntent.putExtra(TYPE, PUSH_ALARM);
-            context.startActivity(activityIntent);
-        }
-    }
-
-    public final static class StopperAlarmReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Intent activityIntent = new Intent(context, LoadtestActivity.class);
-            activityIntent.putExtra(TYPE, STOP);
-            context.startActivity(activityIntent);
-        }
-    }
-
-    private static Map<String, TimeUnit> initializeTimeUnitMap() {
-        Map<String, TimeUnit> labels = new HashMap();
-        labels.put("1m", TimeUnit.ONE_MINUTE);
-        labels.put("5m", TimeUnit.FIVE_MINUTES);
-        labels.put("10m", TimeUnit.TEN_MINUTES);
-        labels.put("30m", TimeUnit.THIRTY_MINUTES);
-        labels.put("1h", TimeUnit.ONE_HOUR);
-        labels.put("3h", TimeUnit.THREE_HOURS);
-        labels.put("6h", TimeUnit.SIX_HOURS);
-        labels.put("12h", TimeUnit.TWELVE_HOURS);
-        labels.put("1d", TimeUnit.ONE_DAY);
-        labels.put("1s", TimeUnit.CTS);
-        return labels;
-    }
-
-    private static List<String> initializeTimeUnitLabels() {
-        List<String> labels = new ArrayList();
-        labels.add("1s");
-        labels.add("1m");
-        labels.add("5m");
-        labels.add("10m");
-        labels.add("30m");
-        labels.add("1h");
-        labels.add("3h");
-        labels.add("6h");
-        labels.add("12h");
-        labels.add("1d");
-        return labels;
-    }
-
-    private AlarmManager mAlarmMgr;
-
-    /**
-     * Used to periodically log atoms to logd.
-     */
-    private PendingIntent mPushPendingIntent;
-
-    /**
-     * Used to end the loadtest.
-     */
-    private PendingIntent mStopPendingIntent;
-
-    private Button mStartStop;
-    private EditText mReplicationText;
-    private Spinner mBucketSpinner;
-    private EditText mPeriodText;
-    private EditText mBurstText;
-    private EditText mDurationText;
-    private TextView mReportText;
-    private CheckBox mPlaceboCheckBox;
-    private CheckBox mCountMetricCheckBox;
-    private CheckBox mDurationMetricCheckBox;
-    private CheckBox mEventMetricCheckBox;
-    private CheckBox mValueMetricCheckBox;
-    private CheckBox mGaugeMetricCheckBox;
-
-    /**
-     * When the load test started.
-     */
-    private long mStartedTimeMillis;
-
-    /**
-     * For measuring perf data.
-     */
-    private PerfData mPerfData;
-
-    /**
-     * For communicating with statsd.
-     */
-    private StatsManager mStatsManager;
-
-    private PowerManager mPowerManager;
-    private WakeLock mWakeLock;
-
-    /**
-     * If true, we only measure the effect of the loadtest infrastructure. No atom are pushed and
-     * the configuration is empty.
-     */
-    private boolean mPlacebo;
-
-    /**
-     * Whether to include CountMetric in the config.
-     */
-    private boolean mIncludeCountMetric;
-
-    /**
-     * Whether to include DurationMetric in the config.
-     */
-    private boolean mIncludeDurationMetric;
-
-    /**
-     * Whether to include EventMetric in the config.
-     */
-    private boolean mIncludeEventMetric;
-
-    /**
-     * Whether to include ValueMetric in the config.
-     */
-    private boolean mIncludeValueMetric;
-
-    /**
-     * Whether to include GaugeMetric in the config.
-     */
-    private boolean mIncludeGaugeMetric;
-
-    /**
-     * The burst size.
-     */
-    private int mBurst;
-
-    /**
-     * The metrics replication.
-     */
-    private int mReplication;
-
-    /**
-     * The period, in seconds, at which batches of atoms are pushed.
-     */
-    private long mPeriodSecs;
-
-    /**
-     * The bucket size, in minutes, for aggregate metrics.
-     */
-    private TimeUnit mBucket;
-
-    /**
-     * The duration, in minutes, of the loadtest.
-     */
-    private long mDurationMins;
-
-    /**
-     * Whether the loadtest has started.
-     */
-    private boolean mStarted = false;
-
-    /**
-     * Orchestrates the logging of pushed events into logd.
-     */
-    private SequencePusher mPusher;
-
-    /**
-     * Generates statsd configs.
-     */
-    private ConfigFactory mFactory;
-
-    /**
-     * For intra-minute periods.
-     */
-    private final Handler mHandler = new Handler();
-
-    /**
-     * Number of metrics in the current config.
-     */
-    private int mNumMetrics;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        Log.d(TAG, "Starting loadtest Activity");
-
-        setContentView(R.layout.activity_loadtest);
-        mReportText = (TextView) findViewById(R.id.report_text);
-        initBurst();
-        initReplication();
-        initBucket();
-        initPeriod();
-        initDuration();
-        initPlacebo();
-        initMetricWhitelist();
-
-        // Hide the keyboard outside edit texts.
-        findViewById(R.id.outside).setOnTouchListener(new View.OnTouchListener() {
-            @Override
-            public boolean onTouch(View v, MotionEvent event) {
-                InputMethodManager imm =
-                        (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
-                if (getCurrentFocus() != null) {
-                    imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
-                }
-                return true;
-            }
-        });
-
-        mStartStop = findViewById(R.id.start_stop);
-        mStartStop.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                if (mStarted) {
-                    stopLoadtest();
-                } else {
-                    startLoadtest();
-                }
-            }
-        });
-
-        mAlarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
-        mStatsManager = (StatsManager) getSystemService("stats");
-        mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
-        mFactory = new ConfigFactory(this);
-        stopLoadtest();
-        mReportText.setText("");
-    }
-
-    @Override
-    public void onNewIntent(Intent intent) {
-        String type = intent.getStringExtra(TYPE);
-        if (type == null) {
-            return;
-        }
-        switch (type) {
-            case PERF_ALARM:
-                onPerfAlarm();
-                break;
-            case PUSH_ALARM:
-                onAlarm();
-                break;
-            case SET_REPLICATION:
-                if (intent.hasExtra(REPLICATION)) {
-                    setReplication(intent.getIntExtra(REPLICATION, 0));
-                }
-                break;
-            case START:
-                startLoadtest();
-                break;
-            case STOP:
-                stopLoadtest();
-                break;
-            default:
-                throw new IllegalArgumentException("Unknown type: " + type);
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        Log.d(TAG, "Destroying");
-        mPerfData.onDestroy();
-        stopLoadtest();
-        clearConfigs();
-        super.onDestroy();
-    }
-
-    @Nullable
-    public StatsdStatsReport getMetadata() {
-        if (!statsdRunning()) {
-            return null;
-        }
-        if (mStatsManager != null) {
-            byte[] data;
-            try {
-                data = mStatsManager.getStatsMetadata();
-            } catch (StatsManager.StatsUnavailableException e) {
-                Log.e(TAG, "Failed to get data from statsd", e);
-                return null;
-            }
-            if (data != null) {
-                StatsdStatsReport report = null;
-                boolean good = false;
-                try {
-                    return StatsdStatsReport.parseFrom(data);
-                } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-                    Log.d(TAG, "Bad StatsdStatsReport");
-                }
-            }
-        }
-        return null;
-    }
-
-    @Nullable
-    public List<ConfigMetricsReport> getData() {
-        if (!statsdRunning()) {
-            return null;
-        }
-        if (mStatsManager != null) {
-            byte[] data;
-            try {
-                data = mStatsManager.getReports(ConfigFactory.CONFIG_ID);
-            } catch (StatsManager.StatsUnavailableException e) {
-                Log.e(TAG, "Failed to get data from statsd", e);
-                return null;
-            }
-            if (data != null) {
-                ConfigMetricsReportList reports = null;
-                try {
-                    reports = ConfigMetricsReportList.parseFrom(data);
-                    Log.d(TAG, "Num reports: " + reports.getReportsCount());
-                    StringBuilder sb = new StringBuilder();
-                    DisplayProtoUtils.displayLogReport(sb, reports);
-                    Log.d(TAG, sb.toString());
-                } catch (com.google.protobuf.InvalidProtocolBufferException e) {
-                    Log.d(TAG, "Invalid data");
-                }
-                if (reports != null) {
-                    return reports.getReportsList();
-                }
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-        String item = parent.getItemAtPosition(position).toString();
-
-        mBucket = TIME_UNIT_MAP.get(item);
-    }
-
-    @Override
-    public void onNothingSelected(AdapterView<?> parent) {
-        // Another interface callback
-    }
-
-    private void onPerfAlarm() {
-        if (mPerfData != null) {
-            mPerfData.onAlarm(this);
-        }
-        // Piggy-back on that alarm to show the elapsed time.
-        long elapsedTimeMins = (long) Math.floor(
-                (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
-        mReportText.setText("Loadtest in progress.\n"
-                + "num metrics =" + mNumMetrics
-                + "\nElapsed time = " + elapsedTimeMins + " min(s)");
-    }
-
-    private void onAlarm() {
-        Log.d(TAG, "ON ALARM");
-
-        // Set the next task.
-        scheduleNext();
-
-        // Do the work.
-        mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StatsdLoadTest");
-        mWakeLock.acquire();
-        if (mPusher != null) {
-            mPusher.next();
-        }
-        mWakeLock.release();
-        mWakeLock = null;
-    }
-
-    /**
-     * Schedules the next cycle of pushing atoms into logd.
-     */
-    private void scheduleNext() {
-        Intent intent = new Intent(this, PusherAlarmReceiver.class);
-        intent.putExtra(TYPE, PUSH_ALARM);
-        mPushPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
-        long nextTime = SystemClock.elapsedRealtime() + mPeriodSecs * 1000;
-        mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mPushPendingIntent);
-    }
-
-    private synchronized void startLoadtest() {
-        if (mStarted) {
-            return;
-        }
-
-        // Clean up the state.
-        stopLoadtest();
-
-        // Prepare to push a sequence of atoms to logd.
-        mPusher = new SequencePusher(mBurst, mPlacebo);
-
-        // Create a config and push it to statsd.
-        if (!setConfig(mFactory.getConfig(mReplication, mBucket, mPlacebo,
-                mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric,
-                mIncludeValueMetric, mIncludeGaugeMetric))) {
-            return;
-        }
-
-        // Remember to stop in the future.
-        Intent intent = new Intent(this, StopperAlarmReceiver.class);
-        intent.putExtra(TYPE, STOP);
-        mStopPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
-        long nextTime = SystemClock.elapsedRealtime() + mDurationMins * 60 * 1000;
-        mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mStopPendingIntent);
-
-        // Log atoms.
-        scheduleNext();
-
-        // Start tracking performance.
-        mPerfData = new PerfData(this, mPlacebo, mReplication, mBucket, mPeriodSecs, mBurst,
-                mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric, mIncludeValueMetric,
-                mIncludeGaugeMetric);
-        mPerfData.startRecording(this);
-
-        mReportText.setText("Loadtest in progress.\nnum metrics =" + mNumMetrics);
-        mStartedTimeMillis = SystemClock.elapsedRealtime();
-
-        updateStarted(true);
-    }
-
-    private synchronized void stopLoadtest() {
-        if (mPushPendingIntent != null) {
-            Log.d(TAG, "Canceling pre-existing push alarm");
-            mAlarmMgr.cancel(mPushPendingIntent);
-            mPushPendingIntent = null;
-        }
-        if (mStopPendingIntent != null) {
-            Log.d(TAG, "Canceling pre-existing stop alarm");
-            mAlarmMgr.cancel(mStopPendingIntent);
-            mStopPendingIntent = null;
-        }
-        if (mWakeLock != null) {
-            mWakeLock.release();
-            mWakeLock = null;
-        }
-        if (mPerfData != null) {
-            mPerfData.stopRecording(this);
-            mPerfData.onDestroy();
-            mPerfData = null;
-        }
-
-        // Obtain the latest data and display it.
-        getData();
-
-        long elapsedTimeMins = (long) Math.floor(
-                (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
-        mReportText.setText("Loadtest ended. Elapsed time = " + elapsedTimeMins + " min(s)");
-        clearConfigs();
-        updateStarted(false);
-    }
-
-    private synchronized void updateStarted(boolean started) {
-        mStarted = started;
-        mStartStop.setBackgroundColor(started ?
-                Color.parseColor("#FFFF0000") : Color.parseColor("#FF00FF00"));
-        mStartStop.setText(started ? getString(R.string.stop) : getString(R.string.start));
-        updateControlsEnabled();
-    }
-
-    private void updateControlsEnabled() {
-        mBurstText.setEnabled(!mPlacebo && !mStarted);
-        mReplicationText.setEnabled(!mPlacebo && !mStarted);
-        mPeriodText.setEnabled(!mStarted);
-        mBucketSpinner.setEnabled(!mPlacebo && !mStarted);
-        mDurationText.setEnabled(!mStarted);
-        mPlaceboCheckBox.setEnabled(!mStarted);
-
-        boolean enabled = !mStarted && !mPlaceboCheckBox.isChecked();
-        mCountMetricCheckBox.setEnabled(enabled);
-        mDurationMetricCheckBox.setEnabled(enabled);
-        mEventMetricCheckBox.setEnabled(enabled);
-        mValueMetricCheckBox.setEnabled(enabled);
-        mGaugeMetricCheckBox.setEnabled(enabled);
-    }
-
-    private boolean statsdRunning() {
-        if (IStatsManager.Stub.asInterface(ServiceManager.getService("stats")) == null) {
-            Log.d(TAG, "Statsd not running");
-            Toast.makeText(LoadtestActivity.this, "Statsd NOT running!", Toast.LENGTH_LONG).show();
-            return false;
-        }
-        return true;
-    }
-
-    private int sanitizeInt(int val, int min, int max) {
-        if (val > max) {
-            val = max;
-        } else if (val < min) {
-            val = min;
-        }
-        return val;
-    }
-
-    private void clearConfigs() {
-        // TODO: Clear all configs instead of specific ones.
-        if (mStatsManager != null) {
-            if (mStarted) {
-                try {
-                    mStatsManager.removeConfig(ConfigFactory.CONFIG_ID);
-                    Log.d(TAG, "Removed loadtest statsd configs.");
-                } catch (StatsManager.StatsUnavailableException e) {
-                    Log.e(TAG, "Failed to remove loadtest configs.", e);
-                }
-            }
-        }
-    }
-
-    private boolean setConfig(ConfigFactory.ConfigMetadata configData) {
-        if (mStatsManager != null) {
-            try {
-                mStatsManager.addConfig(ConfigFactory.CONFIG_ID, configData.bytes);
-                mNumMetrics = configData.numMetrics;
-                Log.d(TAG, "Config pushed to statsd");
-                return true;
-            } catch (StatsManager.StatsUnavailableException | IllegalArgumentException e) {
-                Log.e(TAG, "Failed to push config to statsd", e);
-            }
-        }
-        return false;
-    }
-
-    private synchronized void setReplication(int replication) {
-        if (mStarted) {
-            return;
-        }
-        mReplicationText.setText("" + replication);
-    }
-
-    private synchronized void setPeriodSecs(long periodSecs) {
-        mPeriodSecs = periodSecs;
-    }
-
-    private synchronized void setBurst(int burst) {
-        mBurst = burst;
-    }
-
-    private synchronized void setDurationMins(long durationMins) {
-        mDurationMins = durationMins;
-    }
-
-
-    private void handleFocus(EditText editText) {
-      /*
-        editText.setOnFocusChangeListener(new OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                if (!hasFocus && editText.getText().toString().isEmpty()) {
-                    editText.setText("-1", TextView.BufferType.EDITABLE);
-                }
-            }
-        });
-      */
-    }
-
-    private void initBurst() {
-        mBurst = getResources().getInteger(R.integer.burst_default);
-        mBurstText = (EditText) findViewById(R.id.burst);
-        mBurstText.addTextChangedListener(new NumericalWatcher(mBurstText, 0, 1000) {
-            @Override
-            public void onNewValue(int newValue) {
-                setBurst(newValue);
-            }
-        });
-        handleFocus(mBurstText);
-    }
-
-    private void initReplication() {
-        mReplication = getResources().getInteger(R.integer.replication_default);
-        mReplicationText = (EditText) findViewById(R.id.replication);
-        mReplicationText.addTextChangedListener(new NumericalWatcher(mReplicationText, 1, 4096) {
-            @Override
-            public void onNewValue(int newValue) {
-                mReplication = newValue;
-            }
-        });
-        handleFocus(mReplicationText);
-    }
-
-    private void initBucket() {
-        String defaultValue = getResources().getString(R.string.bucket_default);
-        mBucket = TimeUnit.valueOf(defaultValue);
-        mBucketSpinner = (Spinner) findViewById(R.id.bucket_spinner);
-
-        ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(
-                this, R.layout.spinner_item, TIME_UNIT_LABELS);
-
-        mBucketSpinner.setAdapter(dataAdapter);
-        mBucketSpinner.setOnItemSelectedListener(this);
-
-        for (String label : TIME_UNIT_MAP.keySet()) {
-            if (defaultValue.equals(TIME_UNIT_MAP.get(label).toString())) {
-                mBucketSpinner.setSelection(dataAdapter.getPosition(label));
-            }
-        }
-    }
-
-    private void initPeriod() {
-        mPeriodSecs = getResources().getInteger(R.integer.period_default);
-        mPeriodText = (EditText) findViewById(R.id.period);
-        mPeriodText.addTextChangedListener(new NumericalWatcher(mPeriodText, 1, 60) {
-            @Override
-            public void onNewValue(int newValue) {
-                setPeriodSecs(newValue);
-            }
-        });
-        handleFocus(mPeriodText);
-    }
-
-    private void initDuration() {
-        mDurationMins = getResources().getInteger(R.integer.duration_default);
-        mDurationText = (EditText) findViewById(R.id.duration);
-        mDurationText.addTextChangedListener(new NumericalWatcher(mDurationText, 1, 24 * 60) {
-            @Override
-            public void onNewValue(int newValue) {
-                setDurationMins(newValue);
-            }
-        });
-        handleFocus(mDurationText);
-    }
-
-    private void initPlacebo() {
-        mPlaceboCheckBox = findViewById(R.id.placebo);
-        mPlacebo = false;
-        mPlaceboCheckBox.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                mPlacebo = mPlaceboCheckBox.isChecked();
-                updateControlsEnabled();
-            }
-        });
-    }
-
-    private void initMetricWhitelist() {
-        mCountMetricCheckBox = findViewById(R.id.include_count);
-        mCountMetricCheckBox.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                mIncludeCountMetric = mCountMetricCheckBox.isChecked();
-            }
-        });
-        mDurationMetricCheckBox = findViewById(R.id.include_duration);
-        mDurationMetricCheckBox.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                mIncludeDurationMetric = mDurationMetricCheckBox.isChecked();
-            }
-        });
-        mEventMetricCheckBox = findViewById(R.id.include_event);
-        mEventMetricCheckBox.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                mIncludeEventMetric = mEventMetricCheckBox.isChecked();
-            }
-        });
-        mValueMetricCheckBox = findViewById(R.id.include_value);
-        mValueMetricCheckBox.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                mIncludeValueMetric = mValueMetricCheckBox.isChecked();
-            }
-        });
-        mGaugeMetricCheckBox = findViewById(R.id.include_gauge);
-        mGaugeMetricCheckBox.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                mIncludeGaugeMetric = mGaugeMetricCheckBox.isChecked();
-            }
-        });
-
-        mIncludeCountMetric = mCountMetricCheckBox.isChecked();
-        mIncludeDurationMetric = mDurationMetricCheckBox.isChecked();
-        mIncludeEventMetric = mEventMetricCheckBox.isChecked();
-        mIncludeValueMetric = mValueMetricCheckBox.isChecked();
-        mIncludeGaugeMetric = mGaugeMetricCheckBox.isChecked();
-    }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java
deleted file mode 100644
index 01eebf2..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.loadtest;
-
-import android.annotation.Nullable;
-import android.os.SystemClock;
-import android.util.Log;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/** Parses PSS info from dumpsys meminfo */
-public class MemInfoParser implements PerfParser {
-
-    private static final Pattern LINE_PATTERN =
-        Pattern.compile("\\s*(\\d*,*\\d*)K:\\s(\\S*)\\s\\.*");
-    private static final String PSS_BY_PROCESS = "Total PSS by process:";
-    private static final String TAG = "loadtest.MemInfoParser";
-
-    private boolean mPssStarted;
-    private boolean mPssEnded;
-    private final long mStartTimeMillis;
-
-    public MemInfoParser(long startTimeMillis) {
-        mStartTimeMillis = startTimeMillis;
-    }
-
-    @Override
-    @Nullable
-    public String parseLine(String line) {
-        if (mPssEnded) {
-            return null;
-        }
-        if (!mPssStarted) {
-            if (line.contains(PSS_BY_PROCESS)) {
-                mPssStarted = true;
-            }
-            return null;
-        }
-        if (line.isEmpty()) {
-            mPssEnded = true;
-            return null;
-        }
-        Matcher lineMatcher = LINE_PATTERN.matcher(line);
-        if (lineMatcher.find() && lineMatcher.group(1) != null && lineMatcher.group(2) != null) {
-            if (lineMatcher.group(2).equals("statsd")) {
-                long timeDeltaMillis = SystemClock.elapsedRealtime() - mStartTimeMillis;
-                return timeDeltaMillis + "," + convertToPss(lineMatcher.group(1));
-            }
-        }
-        return null;
-    }
-
-    private String convertToPss(String input) {
-        return input.replace(",", "");
-    }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
deleted file mode 100644
index af7bd4d..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.loadtest;
-
-import android.content.Context;
-import android.os.SystemClock;
-import android.util.Log;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-
-public class MemoryDataRecorder extends PerfDataRecorder {
-    private static final String TAG = "loadtest.MemoryDataDataRecorder";
-    private static final String DUMP_FILENAME = TAG + "_dump.tmp";
-
-    private long mStartTimeMillis;
-    private StringBuilder mSb;
-
-    public MemoryDataRecorder(boolean placebo, int replication, TimeUnit bucket, long periodSecs,
-        int burst,  boolean includeCountMetric, boolean includeDurationMetric,
-        boolean includeEventMetric,  boolean includeValueMetric, boolean includeGaugeMetric) {
-      super(placebo, replication, bucket, periodSecs, burst, includeCountMetric,
-          includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric);
-    }
-
-    @Override
-    public void startRecording(Context context) {
-        mStartTimeMillis = SystemClock.elapsedRealtime();
-        mSb = new StringBuilder();
-    }
-
-    @Override
-    public void onAlarm(Context context) {
-        runDumpsysStats(context, DUMP_FILENAME, "meminfo");
-        readDumpData(context, DUMP_FILENAME, new MemInfoParser(mStartTimeMillis), mSb);
-    }
-
-    @Override
-    public void stopRecording(Context context) {
-        writeData(context, "meminfo_", "time,pss", mSb);
-    }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
deleted file mode 100644
index 555e6dd..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.loadtest;
-
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.widget.TextView;
-
-public abstract class NumericalWatcher implements TextWatcher {
-
-    private static final String TAG = "loadtest.NumericalWatcher";
-
-    private final TextView mTextView;
-    private final int mMin;
-    private final int mMax;
-    private int currentValue = -1;
-
-    public NumericalWatcher(TextView textView, int min, int max) {
-        mTextView = textView;
-        mMin = min;
-        mMax = max;
-    }
-
-    public abstract void onNewValue(int newValue);
-
-    @Override
-    final public void afterTextChanged(Editable editable) {
-        String s = mTextView.getText().toString();
-        if (s.isEmpty()) {
-          return;
-        }
-        int unsanitized = Integer.parseInt(s);
-        int newValue = sanitize(unsanitized);
-        if (currentValue != newValue || unsanitized != newValue) {
-            currentValue = newValue;
-            editable.clear();
-            editable.append(newValue + "");
-        }
-        onNewValue(newValue);
-    }
-
-    @Override
-    final public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
-    @Override
-    final public void onTextChanged(CharSequence s, int start, int before, int count) {}
-
-    private int sanitize(int val) {
-        if (val > mMax) {
-            val = mMax;
-        } else if (val < mMin) {
-            val = mMin;
-        }
-        return val;
-    }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
deleted file mode 100644
index 7a01ade..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.loadtest;
-
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-
-import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/** Prints some information about the device via Dumpsys in order to evaluate health metrics. */
-public class PerfData extends PerfDataRecorder {
-
-    private static final String TAG = "loadtest.PerfData";
-
-    /** Polling period for performance snapshots like memory. */
-    private static final long POLLING_PERIOD_MILLIS = 1 * 60 * 1000;
-
-    public final static class PerfAlarmReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Intent activityIntent = new Intent(context, LoadtestActivity.class);
-            activityIntent.putExtra(LoadtestActivity.TYPE, LoadtestActivity.PERF_ALARM);
-            context.startActivity(activityIntent);
-         }
-    }
-
-    private AlarmManager mAlarmMgr;
-
-    /** Used to periodically poll some dumpsys data. */
-    private PendingIntent mPendingIntent;
-
-    private final Set<PerfDataRecorder> mRecorders;
-
-    public PerfData(LoadtestActivity loadtestActivity, boolean placebo, int replication,
-        TimeUnit bucket, long periodSecs,  int burst, boolean includeCountMetric,
-        boolean includeDurationMetric, boolean includeEventMetric,  boolean includeValueMetric,
-        boolean includeGaugeMetric) {
-      super(placebo, replication, bucket, periodSecs, burst, includeCountMetric,
-          includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric);
-        mRecorders = new HashSet();
-        mRecorders.add(new BatteryDataRecorder(placebo, replication, bucket, periodSecs, burst,
-                includeCountMetric, includeDurationMetric, includeEventMetric, includeValueMetric,
-                includeGaugeMetric));
-        mRecorders.add(new MemoryDataRecorder(placebo, replication, bucket, periodSecs, burst,
-                includeCountMetric, includeDurationMetric, includeEventMetric, includeValueMetric,
-                includeGaugeMetric));
-        mRecorders.add(new StatsdStatsRecorder(loadtestActivity, placebo, replication, bucket,
-                periodSecs, burst, includeCountMetric, includeDurationMetric, includeEventMetric,
-                includeValueMetric, includeGaugeMetric));
-        mRecorders.add(new ValidationRecorder(loadtestActivity, placebo, replication, bucket,
-                periodSecs, burst, includeCountMetric, includeDurationMetric, includeEventMetric,
-                includeValueMetric, includeGaugeMetric));
-        mAlarmMgr = (AlarmManager) loadtestActivity.getSystemService(Context.ALARM_SERVICE);
-    }
-
-    public void onDestroy() {
-        if (mPendingIntent != null) {
-            mAlarmMgr.cancel(mPendingIntent);
-            mPendingIntent = null;
-        }
-    }
-
-    @Override
-    public void startRecording(Context context) {
-        Intent intent = new Intent(context, PerfAlarmReceiver.class);
-        intent.putExtra(LoadtestActivity.TYPE, LoadtestActivity.PERF_ALARM);
-        mPendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
-        mAlarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, -1 /* now */,
-            POLLING_PERIOD_MILLIS, mPendingIntent);
-
-        for (PerfDataRecorder recorder : mRecorders) {
-            recorder.startRecording(context);
-        }
-    }
-
-    @Override
-    public void onAlarm(Context context) {
-        for (PerfDataRecorder recorder : mRecorders) {
-            recorder.onAlarm(context);
-        }
-    }
-
-    @Override
-    public void stopRecording(Context context) {
-        if (mPendingIntent != null) {
-            mAlarmMgr.cancel(mPendingIntent);
-            mPendingIntent = null;
-        }
-
-        for (PerfDataRecorder recorder : mRecorders) {
-            recorder.stopRecording(context);
-        }
-    }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
deleted file mode 100644
index 8613ac1..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.loadtest;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.os.Environment;
-import android.util.Log;
-import android.os.Debug;
-
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import java.io.BufferedReader;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-public abstract class PerfDataRecorder {
-    private static final String TAG = "loadtest.PerfDataRecorder";
-
-    protected final String mTimeAsString;
-    protected final String mColumnSuffix;
-
-    protected PerfDataRecorder(boolean placebo, int replication, TimeUnit bucket, long periodSecs,
-        int burst, boolean includeCountMetric, boolean includeDurationMetric,
-        boolean includeEventMetric,  boolean includeValueMetric, boolean includeGaugeMetric) {
-        mTimeAsString = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date());
-        mColumnSuffix = getColumnSuffix(placebo, replication, bucket, periodSecs, burst,
-            includeCountMetric, includeDurationMetric, includeEventMetric, includeValueMetric,
-            includeGaugeMetric);
-    }
-
-    /** Starts recording performance data. */
-    public abstract void startRecording(Context context);
-
-    /** Called periodically. For the recorder to sample data, if needed. */
-    public abstract void onAlarm(Context context);
-
-    /** Stops recording performance data, and writes it to disk. */
-    public abstract void stopRecording(Context context);
-
-    /** Runs the dumpsys command. */
-    protected void runDumpsysStats(Context context, String dumpFilename, String cmd,
-        String... args) {
-        boolean success = false;
-        // Call dumpsys Dump statistics to a file.
-        FileOutputStream fo = null;
-        try {
-            fo = context.openFileOutput(dumpFilename, Context.MODE_PRIVATE);
-            if (!Debug.dumpService(cmd, fo.getFD(), args)) {
-                Log.w(TAG, "Dumpsys failed.");
-            }
-            success = true;
-        } catch (IOException | SecurityException | NullPointerException e) {
-            // SecurityException may occur when trying to dump multi-user info.
-            // NPE can occur during dumpService  (root cause unknown).
-            throw new RuntimeException(e);
-        } finally {
-            closeQuietly(fo);
-        }
-    }
-
-    /**
-     * Reads a text file and parses each line, one by one. The result of the parsing is stored
-     * in the passed {@link StringBuffer}.
-     */
-    protected void readDumpData(Context context, String dumpFilename, PerfParser parser,
-        StringBuilder sb) {
-        FileInputStream fi = null;
-        BufferedReader br = null;
-        try {
-            fi = context.openFileInput(dumpFilename);
-            br = new BufferedReader(new InputStreamReader(fi));
-            String line = br.readLine();
-            while (line != null) {
-                String recordLine = parser.parseLine(line);
-                if (recordLine != null) {
-                  sb.append(recordLine).append('\n');
-                }
-                line = br.readLine();
-            }
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        } finally {
-            closeQuietly(br);
-        }
-    }
-
-    /** Writes CSV data to a file. */
-    protected void writeData(Context context, String filePrefix, String columnPrefix,
-        StringBuilder sb) {
-        File dataFile = new File(getStorageDir(), filePrefix + mTimeAsString + ".csv");
-
-        FileWriter writer = null;
-        try {
-            writer = new FileWriter(dataFile);
-            writer.append(columnPrefix + mColumnSuffix + "\n");
-            writer.append(sb.toString());
-            writer.flush();
-            Log.d(TAG, "Finished writing data at " + dataFile.getAbsolutePath());
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        } finally {
-            closeQuietly(writer);
-        }
-    }
-
-    /** Gets the suffix to use in the column name for perf data. */
-    private String getColumnSuffix(boolean placebo, int replication, TimeUnit bucket,
-        long periodSecs, int burst, boolean includeCountMetric, boolean includeDurationMetric,
-        boolean includeEventMetric,  boolean includeValueMetric, boolean includeGaugeMetric) {
-        if (placebo) {
-            return "_placebo_p=" + periodSecs;
-        }
-        StringBuilder sb = new StringBuilder()
-            .append("_r=" + replication)
-            .append("_bkt=" + bucket)
-            .append("_p=" + periodSecs)
-            .append("_bst=" + burst)
-            .append("_m=");
-        if (includeCountMetric) {
-            sb.append("c");
-        }
-        if (includeEventMetric) {
-            sb.append("e");
-        }
-        if (includeDurationMetric) {
-            sb.append("d");
-        }
-        if (includeGaugeMetric) {
-            sb.append("g");
-        }
-        if (includeValueMetric) {
-            sb.append("v");
-        }
-        return sb.toString();
-    }
-
-    private File getStorageDir() {
-        File file = new File(Environment.getExternalStoragePublicDirectory(
-            Environment.DIRECTORY_DOCUMENTS), "loadtest/" + mTimeAsString);
-        if (!file.mkdirs()) {
-            Log.e(TAG, "Directory not created");
-        }
-        return file;
-    }
-
-    private void closeQuietly(@Nullable Closeable c) {
-        if (c != null) {
-            try {
-                c.close();
-            } catch (IOException ignore) {
-            }
-        }
-    }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java
deleted file mode 100644
index 5dcce9a..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.loadtest;
-
-import android.util.Log;
-import android.util.StatsLog;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Manages the pushing of atoms into logd for loadtesting.
- * We rely on small number of pushed atoms, and a config with metrics based on those atoms.
- * The atoms are:
- * <ul>
- *   <li> BatteryLevelChanged   - For EventMetric, CountMetric and GaugeMetric (no dimensions).
- *   <li> BleScanResultReceived - For CountMetric and ValueMetric, sliced by uid.
- *   <li> ChargingStateChanged  - For DurationMetric (no dimension).
- *   <li> GpsScanStateChanged   - For DurationMetric, sliced by uid.
- *   <li> ScreenStateChanged    - For Conditions with no dimensions.
- *   <li> AudioStateChanged     - For Conditions with dimensions (uid).
- * </ul>
- * The sequence is played over and over at a given frequency.
- */
-public class SequencePusher {
-    private static final String TAG = "SequencePusher";
-
-    /** Some atoms are pushed in burst of {@code mBurst} events. */
-    private final int mBurst;
-
-    /** If this is true, we don't log anything in logd. */
-    private final boolean mPlacebo;
-
-    /** Current state in the automaton. */
-    private int mCursor = 0;
-
-  public SequencePusher(int burst, boolean placebo) {
-        mBurst = burst;
-        mPlacebo = placebo;
-    }
-
-    /**
-     * Pushes the next atom to logd.
-     * This follows a small automaton which makes the right events and conditions overlap:
-     *   (0)  Push a burst of BatteryLevelChanged atoms.
-     *   (1)  Push a burst of BleScanResultReceived atoms.
-     *   (2)  Push ChargingStateChanged with BATTERY_STATUS_CHARGING once.
-     *   (3)  Push a burst of GpsScanStateChanged atoms with ON, with a different uid each time.
-     *   (4)  Push ChargingStateChanged with BATTERY_STATUS_NOT_CHARGING once.
-     *   (5)  Push a burst GpsScanStateChanged atoms with OFF, with a different uid each time.
-     *   (6)  Push ScreenStateChanged with STATE_ON once.
-     *   (7)  Push a burst of AudioStateChanged with ON, with a different uid each time.
-     *   (8)  Repeat steps (0)-(5).
-     *   (9)  Push ScreenStateChanged with STATE_OFF once.
-     *   (10) Push a burst of AudioStateChanged with OFF, with a different uid each time.
-     * and repeat.
-     */
-    public void next() {
-        Log.d(TAG, "Next step: " + mCursor);
-        if (mPlacebo) {
-            return;
-        }
-        switch (mCursor) {
-            case 0:
-            case 8:
-                for (int i = 0; i < mBurst; i++) {
-                    StatsLog.write(StatsLog.BATTERY_LEVEL_CHANGED, 50 + i /* battery_level */);
-                }
-                break;
-            case 1:
-            case 9:
-                for (int i = 0; i < mBurst; i++) {
-                    StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, i /* uid */,
-                        100 /* num_of_results */);
-                }
-                break;
-            case 2:
-            case 10:
-                StatsLog.write(StatsLog.CHARGING_STATE_CHANGED,
-                    StatsLog.CHARGING_STATE_CHANGED__STATE__BATTERY_STATUS_CHARGING
-                    /* charging_state */);
-                break;
-            case 3:
-            case 11:
-                for (int i = 0; i < mBurst; i++) {
-                    StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, i /* uid */,
-                        StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON /* state */);
-                }
-                break;
-            case 4:
-            case 12:
-                StatsLog.write(StatsLog.CHARGING_STATE_CHANGED,
-                    StatsLog.CHARGING_STATE_CHANGED__STATE__BATTERY_STATUS_NOT_CHARGING
-                    /* charging_state */);
-                break;
-            case 5:
-            case 13:
-                for (int i = 0; i < mBurst; i++) {
-                    StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, i /* uid */,
-                        StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF /* state */);
-                }
-                break;
-            case 6:
-                StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
-                    StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_ON /* display_state */);
-                break;
-            case 7:
-                for (int i = 0; i < mBurst; i++) {
-                    StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, i /* uid */,
-                        StatsLog.AUDIO_STATE_CHANGED__STATE__ON /* state */);
-                }
-                break;
-            case 14:
-                StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
-                    StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF /* display_state */);
-                break;
-            case 15:
-                for (int i = 0; i < mBurst; i++) {
-                    StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, i /* uid */,
-                        StatsLog.AUDIO_STATE_CHANGED__STATE__OFF /* state */);
-                }
-                break;
-            default:
-        }
-        mCursor++;
-        if (mCursor > 15) {
-            mCursor = 0;
-        }
-    }
-
-    /**
-     * Properly finishes in order to be close all conditions and durations.
-     */
-    public void finish() {
-        // Screen goes back to off. This will ensure that conditions get back to false.
-        StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
-            StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF /* display_state */);
-        for (int i = 0; i < mBurst; i++) {
-          StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, i /* uid */,
-              StatsLog.AUDIO_STATE_CHANGED__STATE__OFF /* state */);
-        }
-        // Stop charging, to ensure the corresponding durations are closed.
-        StatsLog.write(StatsLog.CHARGING_STATE_CHANGED,
-            StatsLog.CHARGING_STATE_CHANGED__STATE__BATTERY_STATUS_NOT_CHARGING
-            /* charging_state */);
-        // Stop scanning GPS, to ensure the corresponding conditions get back to false.
-        for (int i = 0; i < mBurst; i++) {
-          StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, i /* uid */,
-              StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF /* state */);
-        }
-    }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
deleted file mode 100644
index 3939e7e..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.loadtest;
-
-import android.content.Context;
-import com.android.os.StatsLog.StatsdStatsReport;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-
-public class StatsdStatsRecorder extends PerfDataRecorder {
-    private static final String TAG = "loadtest.StatsdStatsRecorder";
-
-    private final LoadtestActivity mLoadtestActivity;
-
-    public StatsdStatsRecorder(LoadtestActivity loadtestActivity, boolean placebo, int replication,
-        TimeUnit bucket, long periodSecs, int burst, boolean includeCountMetric,
-        boolean includeDurationMetric, boolean includeEventMetric,  boolean includeValueMetric,
-        boolean includeGaugeMetric) {
-      super(placebo, replication, bucket, periodSecs, burst, includeCountMetric,
-          includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric);
-        mLoadtestActivity = loadtestActivity;
-    }
-
-    @Override
-    public void startRecording(Context context) {
-        // Nothing to do.
-    }
-
-    @Override
-    public void onAlarm(Context context) {
-        // Nothing to do.
-    }
-
-    @Override
-    public void stopRecording(Context context) {
-        StatsdStatsReport metadata = mLoadtestActivity.getMetadata();
-        if (metadata != null) {
-            int numConfigs = metadata.getConfigStatsCount();
-            StringBuilder sb = new StringBuilder();
-            StatsdStatsReport.ConfigStats configStats = metadata.getConfigStats(numConfigs - 1);
-            sb.append("metric_count,")
-                .append(configStats.getMetricCount() + "\n")
-                .append("condition_count,")
-                .append(configStats.getConditionCount() + "\n")
-                .append("matcher_count,")
-                .append(configStats.getMatcherCount() + "\n");
-            writeData(context, "statsdstats_", "stat,value", sb);
-        }
-    }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java
deleted file mode 100644
index d9f0ca9..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.statsd.loadtest;
-
-import android.content.Context;
-import android.util.Log;
-import com.android.os.StatsLog.ConfigMetricsReport;
-import com.android.os.StatsLog.EventMetricData;
-import com.android.os.StatsLog.StatsLogReport;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Checks the correctness of the stats.
- */
-public class ValidationRecorder extends PerfDataRecorder {
-    private static final String TAG = "loadtest.ValidationRecorder";
-
-    private final LoadtestActivity mLoadtestActivity;
-
-    public ValidationRecorder(LoadtestActivity loadtestActivity, boolean placebo, int replication,
-        TimeUnit bucket, long periodSecs, int burst,  boolean includeCountMetric,
-        boolean includeDurationMetric, boolean includeEventMetric,  boolean includeValueMetric,
-        boolean includeGaugeMetric) {
-      super(placebo, replication, bucket, periodSecs, burst, includeCountMetric,
-          includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric);
-        mLoadtestActivity = loadtestActivity;
-    }
-
-    @Override
-    public void startRecording(Context context) {
-        // Nothing to do.
-    }
-
-    @Override
-    public void onAlarm(Context context) {
-        validateData();
-    }
-
-    @Override
-    public void stopRecording(Context context) {
-        validateData();
-    }
-
-    private void validateData() {
-        // The code below is commented out because it calls getData, which has the side-effect
-        // of clearing statsd's data buffer.
-        /*
-        List<ConfigMetricsReport> reports = mLoadtestActivity.getData();
-        if (reports != null) {
-            Log.d(TAG, "GOT DATA");
-            for (ConfigMetricsReport report : reports) {
-                for (StatsLogReport logReport : report.getMetricsList()) {
-                    if (!logReport.hasMetricId()) {
-                        Log.e(TAG, "Metric missing name.");
-                    }
-                }
-            }
-        }
-        */
-    }
-
-    private void validateEventBatteryLevelChanges(StatsLogReport logReport) {
-        Log.d(TAG, "Validating " + logReport.getMetricId());
-        if (logReport.hasEventMetrics()) {
-            Log.d(TAG, "Num events captured: " + logReport.getEventMetrics().getDataCount());
-            for (EventMetricData data : logReport.getEventMetrics().getDataList()) {
-                Log.d(TAG, "  Event : " + data.getAtom());
-            }
-        } else {
-            Log.d(TAG, "Metric is invalid");
-        }
-    }
-
-    private void validateEventBatteryLevelChangesWhileScreenIsOn(StatsLogReport logReport) {
-        Log.d(TAG, "Validating " + logReport.getMetricId());
-    }
-}
diff --git a/config/boot-profile.txt b/config/boot-profile.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/config/boot-profile.txt
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java b/core/java/android/accessibilityservice/AccessibilityGestureInfo.aidl
similarity index 61%
rename from cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java
rename to core/java/android/accessibilityservice/AccessibilityGestureInfo.aidl
index e000918..2539051 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureInfo.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,15 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.statsd.loadtest;
 
-import android.annotation.Nullable;
+package android.accessibilityservice;
 
-public interface PerfParser {
-
-  /**
-   * Parses one line of the dumpsys output, and returns a string to write to the data file,
-   * or null if no string should be written.
-   */
-  @Nullable String parseLine(String line);
-}
+parcelable AccessibilityGestureInfo;
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureInfo.java b/core/java/android/accessibilityservice/AccessibilityGestureInfo.java
new file mode 100644
index 0000000..dc50a4c
--- /dev/null
+++ b/core/java/android/accessibilityservice/AccessibilityGestureInfo.java
@@ -0,0 +1,155 @@
+/*
+ * 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.accessibilityservice;
+
+
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_UP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT_AND_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT_AND_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT_AND_UP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT_AND_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT_AND_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT_AND_UP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class describes the gesture information including gesture id and which display it happens
+ * on.
+ * <p>
+ * <strong>Note:</strong> Accessibility services setting the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
+ * flag can receive gestures.
+ *
+ * @see AccessibilityService#onGesture(AccessibilityGestureInfo)
+ */
+
+public final class AccessibilityGestureInfo implements Parcelable {
+
+    /** @hide */
+    @IntDef(prefix = { "GESTURE_" }, value = {
+            GESTURE_SWIPE_UP,
+            GESTURE_SWIPE_UP_AND_LEFT,
+            GESTURE_SWIPE_UP_AND_DOWN,
+            GESTURE_SWIPE_UP_AND_RIGHT,
+            GESTURE_SWIPE_DOWN,
+            GESTURE_SWIPE_DOWN_AND_LEFT,
+            GESTURE_SWIPE_DOWN_AND_UP,
+            GESTURE_SWIPE_DOWN_AND_RIGHT,
+            GESTURE_SWIPE_LEFT,
+            GESTURE_SWIPE_LEFT_AND_UP,
+            GESTURE_SWIPE_LEFT_AND_RIGHT,
+            GESTURE_SWIPE_LEFT_AND_DOWN,
+            GESTURE_SWIPE_RIGHT,
+            GESTURE_SWIPE_RIGHT_AND_UP,
+            GESTURE_SWIPE_RIGHT_AND_LEFT,
+            GESTURE_SWIPE_RIGHT_AND_DOWN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GestureType {}
+
+    @GestureType
+    private final int mGestureId;
+    private final int mDisplayId;
+
+    /** @hide */
+    @TestApi
+    public AccessibilityGestureInfo(int gestureId, int displayId) {
+        mGestureId = gestureId;
+        mDisplayId = displayId;
+    }
+
+    private AccessibilityGestureInfo(@NonNull Parcel parcel) {
+        mGestureId = parcel.readInt();
+        mDisplayId = parcel.readInt();
+    }
+
+    /**
+     * Returns the display id of the received-gesture display, for use with
+     * {@link android.hardware.display.DisplayManager#getDisplay(int)}.
+     *
+     * @return the display id.
+     */
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    /**
+     * Returns performed gesture id.
+     *
+     * @return the performed gesture id.
+     *
+     */
+    @GestureType public int getGestureId() {
+        return mGestureId;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder stringBuilder = new StringBuilder("AccessibilityGestureInfo[");
+        stringBuilder.append("gestureId: ").append(mGestureId);
+        stringBuilder.append(", ");
+        stringBuilder.append("displayId: ").append(mDisplayId);
+        stringBuilder.append(']');
+        return stringBuilder.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(mGestureId);
+        parcel.writeInt(mDisplayId);
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    public static final @NonNull Parcelable.Creator<AccessibilityGestureInfo> CREATOR =
+            new Parcelable.Creator<AccessibilityGestureInfo>() {
+        public AccessibilityGestureInfo createFromParcel(Parcel parcel) {
+            return new AccessibilityGestureInfo(parcel);
+        }
+
+        public AccessibilityGestureInfo[] newArray(int size) {
+            return new AccessibilityGestureInfo[size];
+        }
+    };
+
+}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 4aafa53..827e540 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -309,7 +309,7 @@
      * Name under which an AccessibilityService component publishes information
      * about itself. This meta-data must reference an XML resource containing an
      * <code>&lt;{@link android.R.styleable#AccessibilityService accessibility-service}&gt;</code>
-     * tag. This is a a sample XML file configuring an accessibility service:
+     * tag. This is a sample XML file configuring an accessibility service:
      * <pre> &lt;accessibility-service
      *     android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
      *     android:packageNames="foo.bar, foo.baz"
@@ -381,7 +381,8 @@
         void onInterrupt();
         void onServiceConnected();
         void init(int connectionId, IBinder windowToken);
-        boolean onGesture(int gestureId);
+        /** The detected gesture information for different displays */
+        boolean onGesture(AccessibilityGestureInfo gestureInfo);
         boolean onKeyEvent(KeyEvent event);
         /** Magnification changed callbacks for different displays */
         void onMagnificationChanged(int displayId, @NonNull Region region,
@@ -514,17 +515,18 @@
     }
 
     /**
-     * Called by the system when the user performs a specific gesture on the
-     * touch screen.
+     * Called by {@link #onGesture(AccessibilityGestureInfo)} when the user performs a specific
+     * gesture on the default display.
      *
      * <strong>Note:</strong> To receive gestures an accessibility service must
      * request that the device is in touch exploration mode by setting the
-     * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
+     * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
      * flag.
      *
      * @param gestureId The unique id of the performed gesture.
      *
      * @return Whether the gesture was handled.
+     * @deprecated Override {@link #onGesture(AccessibilityGestureInfo)} instead.
      *
      * @see #GESTURE_SWIPE_UP
      * @see #GESTURE_SWIPE_UP_AND_LEFT
@@ -543,11 +545,36 @@
      * @see #GESTURE_SWIPE_RIGHT_AND_LEFT
      * @see #GESTURE_SWIPE_RIGHT_AND_DOWN
      */
+    @Deprecated
     protected boolean onGesture(int gestureId) {
         return false;
     }
 
     /**
+     * Called by the system when the user performs a specific gesture on the
+     * specific touch screen.
+     *<p>
+     * <strong>Note:</strong> To receive gestures an accessibility service must
+     * request that the device is in touch exploration mode by setting the
+     * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
+     * flag.
+     *<p>
+     * <strong>Note:</strong> The default implementation calls {@link #onGesture(int)} when the
+     * touch screen is default display.
+     *
+     * @param gestureInfo The information of gesture.
+     *
+     * @return Whether the gesture was handled.
+     *
+     */
+    public boolean onGesture(@NonNull AccessibilityGestureInfo gestureInfo) {
+        if (gestureInfo.getDisplayId() == Display.DEFAULT_DISPLAY) {
+            onGesture(gestureInfo.getGestureId());
+        }
+        return false;
+    }
+
+    /**
      * Callback that allows an accessibility service to observe the key events
      * before they are passed to the rest of the system. This means that the events
      * are first delivered here before they are passed to the device policy, the
@@ -777,8 +804,8 @@
                             callback, handler);
                     mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo);
                 }
-                connection.sendGesture(mGestureStatusCallbackSequence,
-                        new ParceledListSlice<>(steps));
+                connection.dispatchGesture(mGestureStatusCallbackSequence,
+                        new ParceledListSlice<>(steps), gesture.getDisplayId());
             }
         } catch (RemoteException re) {
             throw new RuntimeException(re);
@@ -1673,8 +1700,8 @@
             }
 
             @Override
-            public boolean onGesture(int gestureId) {
-                return AccessibilityService.this.onGesture(gestureId);
+            public boolean onGesture(AccessibilityGestureInfo gestureInfo) {
+                return AccessibilityService.this.onGesture(gestureInfo);
             }
 
             @Override
@@ -1773,8 +1800,9 @@
             mCaller.sendMessage(message);
         }
 
-        public void onGesture(int gestureId) {
-            Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId);
+        @Override
+        public void onGesture(AccessibilityGestureInfo gestureInfo) {
+            Message message = mCaller.obtainMessageO(DO_ON_GESTURE, gestureInfo);
             mCaller.sendMessage(message);
         }
 
@@ -1887,8 +1915,7 @@
 
                 case DO_ON_GESTURE: {
                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        final int gestureId = message.arg1;
-                        mCallback.onGesture(gestureId);
+                        mCallback.onGesture((AccessibilityGestureInfo) message.obj);
                     }
                 } return;
 
diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java
index a3e7ad5..3b79d21 100644
--- a/core/java/android/accessibilityservice/GestureDescription.java
+++ b/core/java/android/accessibilityservice/GestureDescription.java
@@ -23,6 +23,7 @@
 import android.graphics.RectF;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.Display;
 
 import com.android.internal.util.Preconditions;
 
@@ -33,7 +34,7 @@
  * Accessibility services with the
  * {@link android.R.styleable#AccessibilityService_canPerformGestures} property can dispatch
  * gestures. This class describes those gestures. Gestures are made up of one or more strokes.
- * Gestures are immutable once built.
+ * Gestures are immutable once built and will be dispatched to the specified display.
  * <p>
  * Spatial dimensions throughout are in screen pixels. Time is measured in milliseconds.
  */
@@ -48,6 +49,7 @@
 
     private final List<StrokeDescription> mStrokes = new ArrayList<>();
     private final float[] mTempPos = new float[2];
+    private final int mDisplayId;
 
     /**
      * Get the upper limit for the number of strokes a gesture may contain.
@@ -67,10 +69,17 @@
         return MAX_GESTURE_DURATION_MS;
     }
 
-    private GestureDescription() {}
+    private GestureDescription() {
+       this(new ArrayList<>());
+    }
 
     private GestureDescription(List<StrokeDescription> strokes) {
+        this(strokes, Display.DEFAULT_DISPLAY);
+    }
+
+    private GestureDescription(List<StrokeDescription> strokes, int displayId) {
         mStrokes.addAll(strokes);
+        mDisplayId = displayId;
     }
 
     /**
@@ -94,6 +103,16 @@
     }
 
     /**
+     * Returns the ID of the display this gesture is sent on, for use with
+     * {@link android.hardware.display.DisplayManager#getDisplay(int)}.
+     *
+     * @return The logical display id.
+     */
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    /**
      * Return the smallest key point (where a path starts or ends) that is at least a specified
      * offset
      * @param offset the minimum start time
@@ -160,9 +179,10 @@
     public static class Builder {
 
         private final List<StrokeDescription> mStrokes = new ArrayList<>();
+        private int mDisplayId = Display.DEFAULT_DISPLAY;
 
         /**
-         * Add a stroke to the gesture description. Up to
+         * Adds a stroke to the gesture description. Up to
          * {@link GestureDescription#getMaxStrokeCount()} paths may be
          * added to a gesture, and the total gesture duration (earliest path start time to latest
          * path end time) may not exceed {@link GestureDescription#getMaxGestureDuration()}.
@@ -187,11 +207,23 @@
             return this;
         }
 
+        /**
+         * Sets the id of the display to dispatch gestures.
+         *
+         * @param displayId The logical display id
+         *
+         * @return this
+         */
+        public @NonNull Builder setDisplayId(int displayId) {
+            mDisplayId = displayId;
+            return this;
+        }
+
         public GestureDescription build() {
             if (mStrokes.size() == 0) {
                 throw new IllegalStateException("Gestures must have at least one stroke");
             }
-            return new GestureDescription(mStrokes);
+            return new GestureDescription(mStrokes, mDisplayId);
         }
     }
 
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 1dae4fc..407ba59 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -20,6 +20,7 @@
 import android.graphics.Region;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityWindowInfo;
+import android.accessibilityservice.AccessibilityGestureInfo;
 import android.view.KeyEvent;
 
 /**
@@ -35,7 +36,7 @@
 
     void onInterrupt();
 
-    void onGesture(int gesture);
+    void onGesture(in AccessibilityGestureInfo gestureInfo);
 
     void clearAccessibilityCache();
 
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index c8e0132..1ca07dd 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -95,6 +95,8 @@
 
     void sendGesture(int sequence, in ParceledListSlice gestureSteps);
 
+    void dispatchGesture(int sequence, in ParceledListSlice gestureSteps, int displayId);
+
     boolean isFingerprintGestureDetectionAvailable();
 
     IBinder getOverlayWindowToken(int displayid);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 926044b..b8d9575 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -177,6 +177,13 @@
     private static final String KEY_LAUNCH_DISPLAY_ID = "android.activity.launchDisplayId";
 
     /**
+     * The id of the display where the caller was on.
+     * @see #setCallerDisplayId(int)
+     * @hide
+     */
+    private static final String KEY_CALLER_DISPLAY_ID = "android.activity.callerDisplayId";
+
+    /**
      * The windowing mode the activity should be launched into.
      * @hide
      */
@@ -269,6 +276,8 @@
             = "android:activity.remoteAnimationAdapter";
 
     /** @hide */
+    public static final int ANIM_UNDEFINED = -1;
+    /** @hide */
     public static final int ANIM_NONE = 0;
     /** @hide */
     public static final int ANIM_CUSTOM = 1;
@@ -299,7 +308,7 @@
 
     private String mPackageName;
     private Rect mLaunchBounds;
-    private int mAnimationType = ANIM_NONE;
+    private int mAnimationType = ANIM_UNDEFINED;
     private int mCustomEnterResId;
     private int mCustomExitResId;
     private int mCustomInPlaceResId;
@@ -318,6 +327,7 @@
     private int mExitCoordinatorIndex;
     private PendingIntent mUsageTimeReport;
     private int mLaunchDisplayId = INVALID_DISPLAY;
+    private int mCallerDisplayId = INVALID_DISPLAY;
     @WindowConfiguration.WindowingMode
     private int mLaunchWindowingMode = WINDOWING_MODE_UNDEFINED;
     @WindowConfiguration.ActivityType
@@ -896,7 +906,7 @@
             Slog.w(TAG, e);
         }
         mLaunchBounds = opts.getParcelable(KEY_LAUNCH_BOUNDS);
-        mAnimationType = opts.getInt(KEY_ANIM_TYPE);
+        mAnimationType = opts.getInt(KEY_ANIM_TYPE, ANIM_UNDEFINED);
         switch (mAnimationType) {
             case ANIM_CUSTOM:
                 mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
@@ -945,6 +955,7 @@
         }
         mLockTaskMode = opts.getBoolean(KEY_LOCK_TASK_MODE, false);
         mLaunchDisplayId = opts.getInt(KEY_LAUNCH_DISPLAY_ID, INVALID_DISPLAY);
+        mCallerDisplayId = opts.getInt(KEY_CALLER_DISPLAY_ID, INVALID_DISPLAY);
         mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED);
         mLaunchActivityType = opts.getInt(KEY_LAUNCH_ACTIVITY_TYPE, ACTIVITY_TYPE_UNDEFINED);
         mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
@@ -1204,6 +1215,17 @@
     }
 
     /** @hide */
+    public int getCallerDisplayId() {
+        return mCallerDisplayId;
+    }
+
+    /** @hide */
+    public ActivityOptions setCallerDisplayId(int callerDisplayId) {
+        mCallerDisplayId = callerDisplayId;
+        return this;
+    }
+
+    /** @hide */
     public int getLaunchWindowingMode() {
         return mLaunchWindowingMode;
     }
@@ -1447,7 +1469,9 @@
         if (mLaunchBounds != null) {
             b.putParcelable(KEY_LAUNCH_BOUNDS, mLaunchBounds);
         }
-        b.putInt(KEY_ANIM_TYPE, mAnimationType);
+        if (mAnimationType != ANIM_UNDEFINED) {
+            b.putInt(KEY_ANIM_TYPE, mAnimationType);
+        }
         if (mUsageTimeReport != null) {
             b.putParcelable(KEY_USAGE_TIME_REPORT, mUsageTimeReport);
         }
@@ -1506,6 +1530,9 @@
         if (mLaunchDisplayId != INVALID_DISPLAY) {
             b.putInt(KEY_LAUNCH_DISPLAY_ID, mLaunchDisplayId);
         }
+        if (mCallerDisplayId != INVALID_DISPLAY) {
+            b.putInt(KEY_CALLER_DISPLAY_ID, mCallerDisplayId);
+        }
         if (mLaunchWindowingMode != WINDOWING_MODE_UNDEFINED) {
             b.putInt(KEY_LAUNCH_WINDOWING_MODE, mLaunchWindowingMode);
         }
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 755f047..415ec64 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -386,9 +386,15 @@
 
                 // Also report this geometry information to InputMethodManagerService.
                 // TODO(b/115693908): Unify this logic into the above WMS-based one.
+                // TODO(b/138175283): Address the location update when the host of this view is
+                //  moving.
                 final Matrix matrix = new Matrix();
+                final int[] locationOnScreen = new int[2];
+                getLocationOnScreen(locationOnScreen);
+                final int dx = locationOnScreen[0];
+                final int dy = locationOnScreen[1];
                 matrix.set(getMatrix());
-                matrix.postTranslate(x, y);
+                matrix.postTranslate(dx, dy);
                 mContext.getSystemService(InputMethodManager.class)
                         .reportActivityView(displayId, matrix);
             }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index fb72e65..d20cc41 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -834,9 +834,12 @@
     public static final int OP_ACCESS_ACCESSIBILITY = 88;
     /** @hide Read the device identifiers (IMEI / MEID, IMSI, SIM / Build serial) */
     public static final int OP_READ_DEVICE_IDENTIFIERS = 89;
+    /** @hide Query all apps on device, regardless of declarations in the calling app manifest */
+    public static final int OP_QUERY_ALL_PACKAGES = 90;
+
     /** @hide */
     @UnsupportedAppUsage
-    public static final int _NUM_OP = 90;
+    public static final int _NUM_OP = 91;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1112,6 +1115,8 @@
     public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
     /** @hide Read device identifiers */
     public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers";
+    /** @hide Query all packages on device */
+    public static final String OPSTR_QUERY_ALL_PACKAGES = "android:query_all_packages";
 
     // Warning: If an permission is added here it also has to be added to
     // com.android.packageinstaller.permission.utils.EventLogger
@@ -1273,6 +1278,7 @@
             OP_LEGACY_STORAGE,                  // LEGACY_STORAGE
             OP_ACCESS_ACCESSIBILITY,            // ACCESS_ACCESSIBILITY
             OP_READ_DEVICE_IDENTIFIERS,         // READ_DEVICE_IDENTIFIERS
+            OP_QUERY_ALL_PACKAGES,              // QUERY_ALL_PACKAGES
     };
 
     /**
@@ -1369,6 +1375,7 @@
             OPSTR_LEGACY_STORAGE,
             OPSTR_ACCESS_ACCESSIBILITY,
             OPSTR_READ_DEVICE_IDENTIFIERS,
+            OPSTR_QUERY_ALL_PACKAGES,
     };
 
     /**
@@ -1466,6 +1473,7 @@
             "LEGACY_STORAGE",
             "ACCESS_ACCESSIBILITY",
             "READ_DEVICE_IDENTIFIERS",
+            "QUERY_ALL_PACKAGES",
     };
 
     /**
@@ -1564,6 +1572,7 @@
             null, // no permission for OP_LEGACY_STORAGE
             null, // no permission for OP_ACCESS_ACCESSIBILITY
             null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS
+            null, // no permission for OP_QUERY_ALL_PACKAGES
     };
 
     /**
@@ -1662,6 +1671,7 @@
             null, // LEGACY_STORAGE
             null, // ACCESS_ACCESSIBILITY
             null, // READ_DEVICE_IDENTIFIERS
+            null, // QUERY_ALL_PACKAGES
     };
 
     /**
@@ -1759,6 +1769,7 @@
             false, // LEGACY_STORAGE
             false, // ACCESS_ACCESSIBILITY
             false, // READ_DEVICE_IDENTIFIERS
+            false, // QUERY_ALL_PACKAGES
     };
 
     /**
@@ -1855,6 +1866,7 @@
             AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE
             AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY
             AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS
+            AppOpsManager.MODE_DEFAULT, // QUERY_ALL_PACKAGES
     };
 
     /**
@@ -1955,6 +1967,7 @@
             false, // LEGACY_STORAGE
             false, // ACCESS_ACCESSIBILITY
             false, // READ_DEVICE_IDENTIFIERS
+            false, // QUERY_ALL_PACKAGES
     };
 
     /**
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 5aa1f8f..372eab2 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5437,10 +5437,11 @@
         /**
          * Construct a RemoteViews for the display in public contexts like on the lockscreen.
          *
+         * @param isLowPriority is this notification low priority
          * @hide
          */
         @UnsupportedAppUsage
-        public RemoteViews makePublicContentView() {
+        public RemoteViews makePublicContentView(boolean isLowPriority) {
             if (mN.publicVersion != null) {
                 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
                 return builder.createContentView();
@@ -5467,7 +5468,11 @@
             }
             mN.extras = publicExtras;
             RemoteViews view;
-            view = makeNotificationHeader();
+            StandardTemplateParams params = mParams.reset().fillTextsFrom(this);
+            if (isLowPriority) {
+                params.forceDefaultColor();
+            }
+            view = makeNotificationHeader(params);
             view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true);
             mN.extras = savedBundle;
             mN.mLargeIcon = largeIcon;
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 6f7a060..4b4a071 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -893,6 +893,17 @@
             String resolvedType = intent != null ?
                     intent.resolveTypeIfNeeded(context.getContentResolver())
                     : null;
+
+            if (context != null && isActivity()) {
+                // Set the context display id as preferred for this activity launches, so that it
+                // can land on caller's display. Or just brought the task to front at the display
+                // where it was on since it has higher preference.
+                ActivityOptions activityOptions = options != null ? new ActivityOptions(options)
+                        : ActivityOptions.makeBasic();
+                activityOptions.setCallerDisplayId(context.getDisplayId());
+                options = activityOptions.toBundle();
+            }
+
             return ActivityManager.getService().sendIntentSender(
                     mTarget, mWhitelistToken, code, intent, resolvedType,
                     onFinished != null
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 3935628..2b74b99 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.accessibilityservice.AccessibilityGestureInfo;
 import android.accessibilityservice.AccessibilityService.Callbacks;
 import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper;
 import android.accessibilityservice.AccessibilityServiceInfo;
@@ -1232,7 +1233,7 @@
                 }
 
                 @Override
-                public boolean onGesture(int gestureId) {
+                public boolean onGesture(AccessibilityGestureInfo gestureInfo) {
                     /* do nothing */
                     return false;
                 }
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
index 591c418..36f3a1e 100644
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -441,6 +441,43 @@
     }
 
     /**
+     * Checks whether a value set presented by a bitmask has zero or single bit
+     *
+     * @param valueSet the value set presented by a bitmask
+     * @return true if the valueSet contains zero or single bit, otherwise false.
+     */
+    private static boolean hasSingleBit(int valueSet) {
+        return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0);
+    }
+
+    /**
+     * Checks whether the object contains none or single sample rate.
+     *
+     * @return true if the object contains none or single sample rate, otherwise false.
+     */
+    public boolean hasSingleSampleRate() {
+        return hasSingleBit(mSampleRate);
+    }
+
+    /**
+     * Checks whether the object contains none or single bits per sample.
+     *
+     * @return true if the object contains none or single bits per sample, otherwise false.
+     */
+    public boolean hasSingleBitsPerSample() {
+        return hasSingleBit(mBitsPerSample);
+    }
+
+    /**
+     * Checks whether the object contains none or single channel mode.
+     *
+     * @return true if the object contains none or single channel mode, otherwise false.
+     */
+    public boolean hasSingleChannelMode() {
+        return hasSingleBit(mChannelMode);
+    }
+
+    /**
      * Checks whether the audio feeding parameters are same.
      *
      * @param other the codec config to compare against
@@ -451,4 +488,58 @@
                 && other.mBitsPerSample == mBitsPerSample
                 && other.mChannelMode == mChannelMode);
     }
+
+    /**
+     * Checks whether another codec config has the similar feeding parameters.
+     * Any parameters with NONE value will be considered to be a wildcard matching.
+     *
+     * @param other the codec config to compare against
+     * @return true if the audio feeding parameters are similar, otherwise false.
+     */
+    public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) {
+        if (other == null || mCodecType != other.mCodecType) {
+            return false;
+        }
+        int sampleRate = other.mSampleRate;
+        if (mSampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE
+                || sampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE) {
+            sampleRate = mSampleRate;
+        }
+        int bitsPerSample = other.mBitsPerSample;
+        if (mBitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE
+                || bitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) {
+            bitsPerSample = mBitsPerSample;
+        }
+        int channelMode = other.mChannelMode;
+        if (mChannelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE
+                || channelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE) {
+            channelMode = mChannelMode;
+        }
+        return sameAudioFeedingParameters(new BluetoothCodecConfig(
+                mCodecType, /* priority */ 0, sampleRate, bitsPerSample, channelMode,
+                /* specific1 */ 0, /* specific2 */ 0, /* specific3 */ 0,
+                /* specific4 */ 0));
+    }
+
+    /**
+     * Checks whether the codec specific parameters are the same.
+     *
+     * @param other the codec config to compare against
+     * @return true if the codec specific parameters are the same, otherwise false.
+     */
+    public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) {
+        if (other == null && mCodecType != other.mCodecType) {
+            return false;
+        }
+        // Currently we only care about the LDAC Playback Quality at CodecSpecific1
+        switch (mCodecType) {
+            case SOURCE_CODEC_TYPE_LDAC:
+                if (mCodecSpecific1 != other.mCodecSpecific1) {
+                    return false;
+                }
+                // fall through
+            default:
+                return true;
+        }
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
index 58b6aea..58a764a 100644
--- a/core/java/android/bluetooth/BluetoothCodecStatus.java
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.java
@@ -89,6 +89,43 @@
         return Arrays.asList(c1).containsAll(Arrays.asList(c2));
     }
 
+    /**
+     * Checks whether the codec config matches the selectable capabilities.
+     * Any parameters of the codec config with NONE value will be considered a wildcard matching.
+     *
+     * @param codecConfig the codec config to compare against
+     * @return true if the codec config matches, otherwise false
+     */
+    public boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig) {
+        if (codecConfig == null || !codecConfig.hasSingleSampleRate()
+                || !codecConfig.hasSingleBitsPerSample() || !codecConfig.hasSingleChannelMode()) {
+            return false;
+        }
+        for (BluetoothCodecConfig selectableConfig : mCodecsSelectableCapabilities) {
+            if (codecConfig.getCodecType() != selectableConfig.getCodecType()) {
+                continue;
+            }
+            int sampleRate = codecConfig.getSampleRate();
+            if ((sampleRate & selectableConfig.getSampleRate()) == 0
+                    && sampleRate != BluetoothCodecConfig.SAMPLE_RATE_NONE) {
+                continue;
+            }
+            int bitsPerSample = codecConfig.getBitsPerSample();
+            if ((bitsPerSample & selectableConfig.getBitsPerSample()) == 0
+                    && bitsPerSample != BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) {
+                continue;
+            }
+            int channelMode = codecConfig.getChannelMode();
+            if ((channelMode & selectableConfig.getChannelMode()) == 0
+                    && channelMode != BluetoothCodecConfig.CHANNEL_MODE_NONE) {
+                continue;
+            }
+            return true;
+        }
+        return false;
+    }
+
+
     @Override
     public int hashCode() {
         return Objects.hash(mCodecConfig, mCodecsLocalCapabilities,
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 05833b5..5d00f09 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -126,6 +126,17 @@
             "android.bluetooth.headsetclient.profile.action.RESULT";
 
     /**
+     * Intent that notifies about vendor specific event arrival. Events not defined in
+     * HFP spec will be matched with supported vendor event list and this intent will
+     * be broadcasted upon a match. Supported vendor events are of format of
+     * of "+eventCode" or "+eventCode=xxxx" or "+eventCode:=xxxx".
+     * Vendor event can be a response to an vendor specific command or unsolicited.
+     *
+     */
+    public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT =
+            "android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT";
+
+    /**
      * Intent that notifies about the number attached to the last voice tag
      * recorded on AG.
      *
@@ -243,6 +254,28 @@
     public static final String EXTRA_CME_CODE =
             "android.bluetooth.headsetclient.extra.CME_CODE";
 
+    /**
+     * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
+     * indicates vendor ID.
+     */
+    public static final String EXTRA_VENDOR_ID =
+            "android.bluetooth.headsetclient.extra.VENDOR_ID";
+
+     /**
+     * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
+     * indicates vendor event code.
+     */
+    public static final String EXTRA_VENDOR_EVENT_CODE =
+            "android.bluetooth.headsetclient.extra.VENDOR_EVENT_CODE";
+
+     /**
+     * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that
+     * contains full vendor event including event code and full arguments.
+     */
+    public static final String EXTRA_VENDOR_EVENT_FULL_ARGS =
+            "android.bluetooth.headsetclient.extra.VENDOR_EVENT_FULL_ARGS";
+
+
     /* Extras for AG_FEATURES, extras type is boolean */
     // TODO verify if all of those are actually useful
     /**
@@ -588,6 +621,31 @@
     }
 
     /**
+     * Send vendor specific AT command.
+     *
+     * @param device remote device
+     * @param vendorId vendor number by Bluetooth SIG
+     * @param atCommand command to be sent. It start with + prefix and only one command at one time.
+     * @return <code>true</code> if command has been issued successfully; <code>false</code>
+     * otherwise.
+     */
+    public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId,
+                                             String atCommand) {
+        if (DBG) log("sendVendorSpecificCommand()");
+        final IBluetoothHeadsetClient service =
+                getService();
+        if (service != null && isEnabled() && isValidDevice(device)) {
+            try {
+                return service.sendVendorAtCommand(device, vendorId, atCommand);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (service == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
      * Stops voice recognition.
      *
      * @param device remote device
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4d8a0c4..895eba6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2044,6 +2044,30 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Open Mobile API capable UICC-based secure
+     * elements.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SE_OMAPI_UICC = "android.hardware.se.omapi.uicc";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Open Mobile API capable eSE-based secure
+     * elements.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports Open Mobile API capable SD-based secure
+     * elements.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SE_OMAPI_SD = "android.hardware.se.omapi.sd";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports the OpenGL ES
      * <a href="http://www.khronos.org/registry/gles/extensions/ANDROID/ANDROID_extension_pack_es31a.txt">
      * Android Extension Pack</a>.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e690a7f..a4933ef 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -66,6 +66,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.FileUtils;
@@ -2445,6 +2446,8 @@
                 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                 return null;
 
+            } else if (tagName.equals("queries")) {
+                parseQueries(pkg, res, parser, flags, outError);
             } else {
                 Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
                         + " at " + mArchiveSourcePath + " "
@@ -3538,6 +3541,9 @@
             owner.mRequiredAccountType = requiredAccountType;
         }
 
+        owner.mForceQueryable =
+                sa.getBoolean(R.styleable.AndroidManifestApplication_forceQueryable, false);
+
         if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestApplication_debuggable,
                 false)) {
@@ -3953,7 +3959,6 @@
                     ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL;
                 }
                 XmlUtils.skipCurrentTag(parser);
-
             } else {
                 if (!RIGID_PARSER) {
                     Slog.w(TAG, "Unknown element under <application>: " + tagName
@@ -4000,6 +4005,67 @@
         return true;
     }
 
+    private boolean parseQueries(Package owner, Resources res, XmlResourceParser parser, int flags,
+            String[] outError)
+            throws IOException, XmlPullParserException {
+
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            if (parser.getName().equals("intent")) {
+                QueriesIntentInfo intentInfo = new QueriesIntentInfo();
+                if (!parseIntent(res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/,
+                        intentInfo, outError)) {
+                    return false;
+                }
+                Intent intent = new Intent();
+                if (intentInfo.countActions() != 1) {
+                    outError[0] = "intent tags must contain exactly one action.";
+                    return false;
+                }
+                intent.setAction(intentInfo.getAction(0));
+                for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
+                    intent.addCategory(intentInfo.getCategory(i));
+                }
+                Uri data = null;
+                String dataType = null;
+                if (intentInfo.countDataTypes() > 1) {
+                    outError[0] = "intent tag may have at most one data type.";
+                    return false;
+                }
+                if (intentInfo.countDataSchemes() > 1) {
+                    outError[0] = "intent tag may have at most one data scheme.";
+                    return false;
+                }
+                if (intentInfo.countDataTypes() == 1) {
+                    data = Uri.fromParts(intentInfo.getDataType(0), "", null);
+                }
+                if (intentInfo.countDataSchemes() == 1) {
+                    dataType = intentInfo.getDataScheme(0);
+                }
+                intent.setDataAndType(data, dataType);
+                owner.mQueriesIntents = ArrayUtils.add(owner.mQueriesIntents, intent);
+            } else if (parser.getName().equals("package")) {
+                final TypedArray sa = res.obtainAttributes(parser,
+                        com.android.internal.R.styleable.AndroidManifestQueriesPackage);
+                final String packageName =
+                        sa.getString(R.styleable.AndroidManifestQueriesPackage_name);
+                if (TextUtils.isEmpty(packageName)) {
+                    outError[0] = "Package name is missing from package tag.";
+                    return false;
+                }
+                owner.mQueriesPackages =
+                        ArrayUtils.add(owner.mQueriesPackages, packageName.intern());
+            }
+        }
+        return true;
+    }
+
     /**
      * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
      */
@@ -6514,6 +6580,9 @@
         // The major version code declared for this package.
         public int mVersionCodeMajor;
 
+        // Whether the package declares that it should be queryable by all normal apps on device.
+        public boolean mForceQueryable;
+
         // Return long containing mVersionCode and mVersionCodeMajor.
         public long getLongVersionCode() {
             return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode);
@@ -6619,6 +6688,9 @@
         /** Whether or not the package is a stub and must be replaced by the full version. */
         public boolean isStub;
 
+        public ArrayList<String> mQueriesPackages;
+        public ArrayList<Intent> mQueriesIntents;
+
         @UnsupportedAppUsage
         public Package(String packageName) {
             this.packageName = packageName;
@@ -7122,6 +7194,9 @@
             use32bitAbi = (dest.readInt() == 1);
             restrictUpdateHash = dest.createByteArray();
             visibleToInstantApps = dest.readInt() == 1;
+            mForceQueryable = dest.readBoolean();
+            mQueriesIntents = dest.createTypedArrayList(Intent.CREATOR);
+            mQueriesPackages = dest.createStringArrayList();
         }
 
         private static void internStringArrayList(List<String> list) {
@@ -7247,6 +7322,9 @@
             dest.writeInt(use32bitAbi ? 1 : 0);
             dest.writeByteArray(restrictUpdateHash);
             dest.writeInt(visibleToInstantApps ? 1 : 0);
+            dest.writeBoolean(mForceQueryable);
+            dest.writeTypedList(mQueriesIntents);
+            dest.writeList(mQueriesPackages);
         }
 
 
@@ -8257,6 +8335,8 @@
         }
     }
 
+    public static final class QueriesIntentInfo extends IntentInfo {}
+
     public final static class ActivityIntentInfo extends IntentInfo {
         @UnsupportedAppUsage
         public Activity activity;
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index af66dc1..d8110f3 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -129,6 +129,25 @@
     }
 
     /**
+     * @hide
+     * @param userId
+     * @return
+     */
+    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+    public boolean hasEnrolledBiometrics(int userId) {
+        if (mService != null) {
+            try {
+                return mService.hasEnrolledBiometrics(userId);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Remote exception in hasEnrolledBiometrics(): " + e);
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    /**
      * Listens for changes to biometric eligibility on keyguard from user settings.
      * @param callback
      * @hide
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 18c14cb..f0a0b2f 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -42,6 +42,9 @@
     // Checks if biometrics can be used.
     int canAuthenticate(String opPackageName, int userId);
 
+    // Checks if any biometrics are enrolled.
+    boolean hasEnrolledBiometrics(int userId);
+
     // Register callback for when keyguard biometric eligibility changes.
     void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
 
diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java
index 623d5ec..f4fd1b6 100644
--- a/core/java/android/hardware/radio/ProgramList.java
+++ b/core/java/android/hardware/radio/ProgramList.java
@@ -369,6 +369,33 @@
         public boolean areModificationsExcluded() {
             return mExcludeModifications;
         }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mIdentifierTypes, mIdentifiers, mIncludeCategories,
+                    mExcludeModifications);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (!(obj instanceof Filter)) return false;
+            Filter other = (Filter) obj;
+
+            if (mIncludeCategories != other.mIncludeCategories) return false;
+            if (mExcludeModifications != other.mExcludeModifications) return false;
+            if (!Objects.equals(mIdentifierTypes, other.mIdentifierTypes)) return false;
+            if (!Objects.equals(mIdentifiers, other.mIdentifiers)) return false;
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "Filter [mIdentifierTypes=" + mIdentifierTypes
+                    + ", mIdentifiers=" + mIdentifiers
+                    + ", mIncludeCategories=" + mIncludeCategories
+                    + ", mExcludeModifications=" + mExcludeModifications + "]";
+        }
     }
 
     /**
@@ -436,5 +463,24 @@
         public @NonNull Set<ProgramSelector.Identifier> getRemoved() {
             return mRemoved;
         }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (!(obj instanceof Chunk)) return false;
+            Chunk other = (Chunk) obj;
+
+            if (mPurge != other.mPurge) return false;
+            if (mComplete != other.mComplete) return false;
+            if (!Objects.equals(mModified, other.mModified)) return false;
+            if (!Objects.equals(mRemoved, other.mRemoved)) return false;
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "Chunk [mPurge=" + mPurge + ", mComplete=" + mComplete
+                    + ", mModified=" + mModified + ", mRemoved=" + mRemoved + "]";
+        }
     }
 }
diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java
index b60c136..c135c8a 100644
--- a/core/java/android/hardware/radio/RadioMetadata.java
+++ b/core/java/android/hardware/radio/RadioMetadata.java
@@ -258,6 +258,11 @@
     private final Bundle mBundle;
 
     @Override
+    public int hashCode() {
+        return mBundle.hashCode();
+    }
+
+    @Override
     public boolean equals(Object obj) {
         if (this == obj) return true;
         if (!(obj instanceof RadioMetadata)) return false;
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 8355e08..2a4576a 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -581,12 +581,16 @@
     public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException {
         if (data == null) return null;
         MemoryFile file = new MemoryFile(name, data.length);
-        if (data.length > 0) {
-            file.writeBytes(data, 0, 0, data.length);
+        try {
+            if (data.length > 0) {
+                file.writeBytes(data, 0, 0, data.length);
+            }
+            file.deactivate();
+            FileDescriptor fd = file.getFileDescriptor();
+            return fd != null ? ParcelFileDescriptor.dup(fd) : null;
+        } finally {
+            file.close();
         }
-        file.deactivate();
-        FileDescriptor fd = file.getFileDescriptor();
-        return fd != null ? ParcelFileDescriptor.dup(fd) : null;
     }
 
     /**
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 5a9ab38..6aef5a5 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -40,6 +40,7 @@
     public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
     public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
     public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
+    public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
 
     private static final Map<String, String> DEFAULT_FLAGS;
 
@@ -56,6 +57,7 @@
         DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
         DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false");
         DEFAULT_FLAGS.put("settings_skip_direction_mutable", "true");
+        DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "false");
     }
 
     /**
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 956161a..955be8d 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -77,23 +77,9 @@
     void hideCurrentInputMethod();
 
     /**
-     * Set a state for controller whether would like to cancel recents animations with deferred
-     * task screenshot presentation.
-     *
-     * When we cancel the recents animation due to a stack order change, we can't just cancel it
-     * immediately as it would lead to a flicker in Launcher if we just remove the task from the
-     * leash. Instead we screenshot the previous task and replace the child of the leash with the
-     * screenshot, so that Launcher can still control the leash lifecycle & make the next app
-     * transition animate smoothly without flickering.
-     *
-     * @param screenshot When set {@code true}, means recents animation will be canceled when the
-     *                   next app launch. System will take previous task's screenshot when the next
-     *                   app transition starting, and skip previous task's animation.
-     *                   Set {@code false} means will not take screenshot & skip animation
-     *                   for previous task.
-     *
-     * @see #cleanupScreenshot()
-     * @see IRecentsAnimationRunner#onCancelled
+     * This call is deprecated, use #setDeferCancelUntilNextTransition() instead
+     * TODO(138144750): Remove this method once there are no callers
+     * @deprecated
      */
     void setCancelWithDeferredScreenshot(boolean screenshot);
 
@@ -104,4 +90,34 @@
      * @see {@link IRecentsAnimationRunner#onAnimationCanceled}
      */
     void cleanupScreenshot();
+
+    /**
+     * Set a state for controller whether would like to cancel recents animations with deferred
+     * task screenshot presentation.
+     *
+     * When we cancel the recents animation due to a stack order change, we can't just cancel it
+     * immediately as it would lead to a flicker in Launcher if we just remove the task from the
+     * leash. Instead we screenshot the previous task and replace the child of the leash with the
+     * screenshot, so that Launcher can still control the leash lifecycle & make the next app
+     * transition animate smoothly without flickering.
+     *
+     * @param defer When set {@code true}, means that the recents animation will defer canceling the
+     *              animation when a stack order change is triggered until the subsequent app
+     *              transition start and skip previous task's animation.
+     *              When set to {@code false}, means that the recents animation will be canceled
+     *              immediately when the stack order changes.
+     * @param screenshot When set {@code true}, means that the system will take previous task's
+     *                   screenshot and replace the contents of the leash with it when the next app
+     *                   transition starting. The runner must call #cleanupScreenshot() to end the
+     *                   recents animation.
+     *                   When set to {@code false}, means that the system will simply wait for the
+     *                   next app transition start to immediately cancel the recents animation. This
+     *                   can be useful when you want an immediate transition into a state where the
+     *                   task is shown in the home/recents activity (without waiting for a
+     *                   screenshot).
+     *
+     * @see #cleanupScreenshot()
+     * @see IRecentsAnimationRunner#onCancelled
+     */
+    void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot);
 }
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl
index 9c652a8..6cda60c 100644
--- a/core/java/android/view/IRecentsAnimationRunner.aidl
+++ b/core/java/android/view/IRecentsAnimationRunner.aidl
@@ -36,7 +36,7 @@
      * @param deferredWithScreenshot If set to {@code true}, the contents of the task will be
      *                               replaced with a screenshot, such that the runner's leash is
      *                               still active. As soon as the runner doesn't need the leash
-     *                               anymore, it can call
+     *                               anymore, it must call
      *                               {@link IRecentsAnimationController#cleanupScreenshot).
      *
      * @see {@link RecentsAnimationController#cleanupScreenshot}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index a01e15e..aa29c5f 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -28,6 +28,7 @@
 import android.graphics.RenderNode;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.util.Log;
 import android.view.Surface.OutOfResourcesException;
 import android.view.View.AttachInfo;
 import android.view.animation.AnimationUtils;
@@ -674,11 +675,11 @@
 
         int syncResult = syncAndDrawFrame(choreographer.mFrameInfo);
         if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
-            setEnabled(false);
-            attachInfo.mViewRootImpl.mSurface.release();
-            // Invalidate since we failed to draw. This should fetch a Surface
-            // if it is still needed or do nothing if we are no longer drawing
-            attachInfo.mViewRootImpl.invalidate();
+            Log.w("OpenGLRenderer", "Surface lost, forcing relayout");
+            // We lost our surface. For a relayout next frame which should give us a new
+            // surface from WindowManager, which hopefully will work.
+            attachInfo.mViewRootImpl.mForceNextWindowRelayout = true;
+            attachInfo.mViewRootImpl.requestLayout();
         }
         if ((syncResult & SYNC_REDRAW_REQUESTED) != 0) {
             attachInfo.mViewRootImpl.invalidate();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 69884dc..45309bb 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -967,6 +967,19 @@
      */
     static boolean sBrokenInsetsDispatch;
 
+    /**
+     * Prior to Q, calling
+     * {@link com.android.internal.policy.DecorView#setBackgroundDrawable(Drawable)}
+     * did not call update the window format so the opacity of the background was not correctly
+     * applied to the window. Some applications rely on this misbehavior to work properly.
+     * <p>
+     * From Q, {@link com.android.internal.policy.DecorView#setBackgroundDrawable(Drawable)} is
+     * the same as {@link com.android.internal.policy.DecorView#setWindowBackground(Drawable)}
+     * which updates the window format.
+     * @hide
+     */
+    protected static boolean sBrokenWindowBackground;
+
     /** @hide */
     @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
     @Retention(RetentionPolicy.SOURCE)
@@ -5208,6 +5221,8 @@
             sBrokenInsetsDispatch = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
                     || targetSdkVersion < Build.VERSION_CODES.Q;
 
+            sBrokenWindowBackground = targetSdkVersion < Build.VERSION_CODES.Q;
+
             sCompatibilityDone = true;
         }
     }
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index efd5daf..a428fea 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -921,10 +921,12 @@
                 if (!mFlingScroller.isFinished()) {
                     mFlingScroller.forceFinished(true);
                     mAdjustScroller.forceFinished(true);
+                    onScrollerFinished(mFlingScroller);
                     onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                 } else if (!mAdjustScroller.isFinished()) {
                     mFlingScroller.forceFinished(true);
                     mAdjustScroller.forceFinished(true);
+                    onScrollerFinished(mAdjustScroller);
                 } else if (mLastDownEventY < mTopSelectionDividerTop) {
                     postChangeCurrentByOneFromLongPress(
                             false, ViewConfiguration.getLongPressTimeout());
@@ -2556,14 +2558,16 @@
                             }
                             return false;
                         }
-                        case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+                        case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+                        case R.id.accessibilityActionScrollDown: {
                             if (NumberPicker.this.isEnabled()
                                     && (getWrapSelectorWheel() || getValue() < getMaxValue())) {
                                 changeValueByOne(true);
                                 return true;
                             }
                         } return false;
-                        case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+                        case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+                        case R.id.accessibilityActionScrollUp: {
                             if (NumberPicker.this.isEnabled()
                                     && (getWrapSelectorWheel() || getValue() > getMinValue())) {
                                 changeValueByOne(false);
@@ -2865,10 +2869,13 @@
             }
             if (NumberPicker.this.isEnabled()) {
                 if (getWrapSelectorWheel() || getValue() < getMaxValue()) {
-                    info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+                    info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+                    info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
                 }
                 if (getWrapSelectorWheel() || getValue() > getMinValue()) {
-                    info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+                    info.addAction(
+                            AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+                    info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
                 }
             }
 
diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java
index 80ea363..d36f343 100644
--- a/core/java/android/widget/ViewAnimator.java
+++ b/core/java/android/widget/ViewAnimator.java
@@ -57,6 +57,9 @@
         super(context, attrs);
 
         TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ViewAnimator);
+        saveAttributeDataForStyleable(context, com.android.internal.R.styleable.ViewAnimator,
+                attrs, a, 0, 0);
+
         int resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_inAnimation, 0);
         if (resource > 0) {
             setInAnimation(context, resource);
@@ -90,6 +93,8 @@
         // attribute to override.
         final TypedArray a = context.obtainStyledAttributes(attrs,
                 com.android.internal.R.styleable.FrameLayout);
+        saveAttributeDataForStyleable(context, com.android.internal.R.styleable.FrameLayout,
+                attrs, a, 0, 0);
         final boolean measureAllChildren = a.getBoolean(
                 com.android.internal.R.styleable.FrameLayout_measureAllChildren, true);
         setMeasureAllChildren(measureAllChildren);
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index ee4666f..8fe2316 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -194,6 +194,38 @@
     public static final String ASSIST_HANDLES_SHOW_WHEN_TAUGHT = "assist_handles_show_when_taught";
 
     /**
+     * (long) Duration per pixel, in milliseconds, of scrolling text at fast speed.
+     */
+    public static final String ASSIST_TRANSCRIPTION_DURATION_PER_PX_FAST =
+            "assist_transcription_duration_per_px_fast";
+
+    /**
+     * (long) Duration per pixel, in milliseconds, of scrolling text at regular speed.
+     */
+    public static final String ASSIST_TRANSCRIPTION_DURATION_PER_PX_REGULAR =
+            "assist_transcription_duration_per_px_regular";
+
+    /**
+     * (long) Duration, in milliseconds, over which text fades in.
+     */
+    public static final String ASSIST_TRANSCRIPTION_FADE_IN_DURATION =
+            "assist_transcription_fade_in_duration";
+
+    /**
+     * (long) Maximum total duration, in milliseconds, for a given transcription.
+     */
+    public static final String ASSIST_TRANSCRIPTION_MAX_DURATION =
+            "assist_transcription_max_duration";
+
+    /**
+     * (long) Minimum total duration, in milliseconds, for a given transcription.
+     */
+    public static final String ASSIST_TRANSCRIPTION_MIN_DURATION =
+            "assist_transcription_min_duration";
+
+    // Flags related to brightline falsing
+
+    /**
      * (bool) Whether to use the new BrightLineFalsingManager.
      */
     public static final String BRIGHTLINE_FALSING_MANAGER_ENABLED =
@@ -282,5 +314,6 @@
             "brightline_falsing_zigzag_y_secondary_deviance";
 
 
-    private SystemUiDeviceConfigFlags() { }
+    private SystemUiDeviceConfigFlags() {
+    }
 }
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index 3ac58e0..d53fada 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -27,6 +27,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.File;
 import java.io.FileInputStream;
 import java.util.Iterator;
 
@@ -38,6 +39,7 @@
     private static int sKernelWakelockUpdateVersion = 0;
     private static final String sWakelockFile = "/proc/wakelocks";
     private static final String sWakeupSourceFile = "/d/wakeup_sources";
+    private static final String sSysClassWakeupDir = "/sys/class/wakeup";
 
     private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
         Process.PROC_TAB_TERM|Process.PROC_OUT_STRING|                // 0: name
@@ -71,99 +73,108 @@
      * @return the updated data.
      */
     public final KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) {
-        byte[] buffer = new byte[32*1024];
-        int len = 0;
-        boolean wakeup_sources;
-        final long startTime = SystemClock.uptimeMillis();
+        boolean useSystemSuspend = (new File(sSysClassWakeupDir)).exists();
 
-        final int oldMask = StrictMode.allowThreadDiskReadsMask();
-        try {
-            FileInputStream is;
-            try {
-                is = new FileInputStream(sWakelockFile);
-                wakeup_sources = false;
-            } catch (java.io.FileNotFoundException e) {
+        if (useSystemSuspend) {
+            WakeLockInfo[] wlStats = null;
+            if (mSuspendControlService == null) {
                 try {
-                    is = new FileInputStream(sWakeupSourceFile);
-                    wakeup_sources = true;
-                } catch (java.io.FileNotFoundException e2) {
-                    Slog.wtf(TAG, "neither " + sWakelockFile + " nor " +
-                            sWakeupSourceFile + " exists");
+                    mSuspendControlService = ISuspendControlService.Stub.asInterface(
+                        ServiceManager.getServiceOrThrow("suspend_control"));
+                } catch (ServiceNotFoundException e) {
+                    Slog.wtf(TAG, "Required service suspend_control not available", e);
                     return null;
                 }
             }
 
-            int cnt;
-            while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) {
-                len += cnt;
+            try {
+                wlStats = mSuspendControlService.getWakeLockStats();
+                updateVersion(staleStats);
+                updateWakelockStats(wlStats, staleStats);
+            } catch (RemoteException e) {
+                Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e);
+                return null;
             }
 
-            is.close();
-        } catch (java.io.IOException e) {
-            Slog.wtf(TAG, "failed to read kernel wakelocks", e);
-            return null;
-        } finally {
-            StrictMode.setThreadPolicyMask(oldMask);
-        }
+            return removeOldStats(staleStats);
 
-        final long readTime = SystemClock.uptimeMillis() - startTime;
-        if (readTime > 100) {
-            Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms");
-        }
+        } else {
+            byte[] buffer = new byte[32*1024];
+            int len = 0;
+            boolean wakeup_sources;
+            final long startTime = SystemClock.uptimeMillis();
 
-        if (len > 0) {
-            if (len >= buffer.length) {
-                Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
+            final int oldMask = StrictMode.allowThreadDiskReadsMask();
+            try {
+                FileInputStream is;
+                try {
+                    is = new FileInputStream(sWakelockFile);
+                    wakeup_sources = false;
+                } catch (java.io.FileNotFoundException e) {
+                    try {
+                        is = new FileInputStream(sWakeupSourceFile);
+                        wakeup_sources = true;
+                    } catch (java.io.FileNotFoundException e2) {
+                        Slog.wtf(TAG, "neither " + sWakelockFile + " nor " +
+                                sWakeupSourceFile + " exists");
+                        return null;
+                    }
+                }
+
+                int cnt;
+                while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) {
+                    len += cnt;
+                }
+
+                is.close();
+            } catch (java.io.IOException e) {
+                Slog.wtf(TAG, "failed to read kernel wakelocks", e);
+                return null;
+            } finally {
+                StrictMode.setThreadPolicyMask(oldMask);
             }
-            int i;
-            for (i=0; i<len; i++) {
-                if (buffer[i] == '\0') {
-                    len = i;
-                    break;
+
+            final long readTime = SystemClock.uptimeMillis() - startTime;
+            if (readTime > 100) {
+                Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms");
+            }
+
+            if (len > 0) {
+                if (len >= buffer.length) {
+                    Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
+                }
+                int i;
+                for (i=0; i<len; i++) {
+                    if (buffer[i] == '\0') {
+                        len = i;
+                        break;
+                    }
                 }
             }
+
+            updateVersion(staleStats);
+            parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
+            return removeOldStats(staleStats);
         }
-
-        updateVersion(staleStats);
-
-        parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
-
-        if (mSuspendControlService == null) {
-            try {
-                mSuspendControlService = ISuspendControlService.Stub.asInterface(
-                    ServiceManager.getServiceOrThrow("suspend_control"));
-            } catch (ServiceNotFoundException e) {
-                Slog.wtf(TAG, "Required service suspend_control not available", e);
-            }
-        }
-
-        try {
-            WakeLockInfo[] wlStats = mSuspendControlService.getWakeLockStats();
-            getNativeWakelockStats(wlStats, staleStats);
-        } catch (RemoteException e) {
-            Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e);
-        }
-
-        return removeOldStats(staleStats);
     }
 
     /**
-     * Reads native wakelock stats from SystemSuspend and updates staleStats with the new
-     * information.
+     * Updates statleStats with stats from  SystemSuspend.
      * @param staleStats Existing object to update.
      * @return the updated stats.
      */
     @VisibleForTesting
-    public KernelWakelockStats getNativeWakelockStats(WakeLockInfo[] wlStats,
+    public KernelWakelockStats updateWakelockStats(WakeLockInfo[] wlStats,
                                                       final KernelWakelockStats staleStats) {
         for (WakeLockInfo info : wlStats) {
             if (!staleStats.containsKey(info.name)) {
                 staleStats.put(info.name, new KernelWakelockStats.Entry((int) info.activeCount,
-                        info.totalTime, sKernelWakelockUpdateVersion));
+                        info.totalTime * 1000 /* ms to us */, sKernelWakelockUpdateVersion));
             } else {
                 KernelWakelockStats.Entry kwlStats = staleStats.get(info.name);
                 kwlStats.mCount = (int) info.activeCount;
-                kwlStats.mTotalTime = info.totalTime;
+                // Convert milliseconds to microseconds
+                kwlStats.mTotalTime = info.totalTime * 1000;
                 kwlStats.mVersion = sKernelWakelockUpdateVersion;
             }
         }
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 599c354..981d0bb 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -989,7 +989,9 @@
             } else {
                 mBackgroundPadding.setEmpty();
             }
-            drawableChanged();
+            if (!View.sBrokenWindowBackground) {
+                drawableChanged();
+            }
         }
     }
 
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index c46f867..2779be6 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -297,10 +297,10 @@
 
     private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition,
             int bottomPosition, int dividerMax) {
-        maybeAddTarget(topPosition, topPosition - mInsets.top);
+        maybeAddTarget(topPosition, topPosition - getStartInset());
         addMiddleTarget(isHorizontalDivision);
-        maybeAddTarget(bottomPosition, dividerMax - mInsets.bottom
-                - (bottomPosition + mDividerSize));
+        maybeAddTarget(bottomPosition,
+                dividerMax - getEndInset() - (bottomPosition + mDividerSize));
     }
 
     private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) {
diff --git a/core/java/com/android/internal/widget/MediaNotificationView.java b/core/java/com/android/internal/widget/MediaNotificationView.java
index e7d240a..0d87afa 100644
--- a/core/java/com/android/internal/widget/MediaNotificationView.java
+++ b/core/java/com/android/internal/widget/MediaNotificationView.java
@@ -26,6 +26,8 @@
 import android.widget.ImageView;
 import android.widget.RemoteViews;
 
+import java.util.ArrayList;
+
 /**
  * A TextView that can float around an image on the end.
  *
@@ -42,6 +44,7 @@
     private View mMainColumn;
     private View mMediaContent;
     private int mImagePushIn;
+    private ArrayList<VisibilityChangeListener> mListeners;
 
     public MediaNotificationView(Context context) {
         this(context, null);
@@ -168,4 +171,48 @@
         mMainColumn = findViewById(com.android.internal.R.id.notification_main_column);
         mMediaContent = findViewById(com.android.internal.R.id.notification_media_content);
     }
+
+    @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        super.onVisibilityAggregated(isVisible);
+        for (int i = 0; i < mListeners.size(); i++) {
+            mListeners.get(i).onAggregatedVisibilityChanged(isVisible);
+        }
+    }
+
+    /**
+     * Add a listener to receive updates on the visibility of this view
+     *
+     * @param listener The listener to add.
+     */
+    public void addVisibilityListener(VisibilityChangeListener listener) {
+        if (mListeners == null) {
+            mListeners = new ArrayList<>();
+        }
+        if (!mListeners.contains(listener)) {
+            mListeners.add(listener);
+        }
+    }
+
+    /**
+     * Remove the specified listener
+     *
+     * @param listener The listener to remove.
+     */
+    public void removeVisibilityListener(VisibilityChangeListener listener) {
+        if (mListeners != null) {
+            mListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Interface for receiving updates when the view's visibility changes
+     */
+    public interface VisibilityChangeListener {
+        /**
+         * Method called when the visibility of this view has changed
+         * @param isVisible true if the view is now visible
+         */
+        void onAggregatedVisibilityChanged(boolean isVisible);
+    }
 }
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index c3e7a36..13e1dfa 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -259,6 +259,8 @@
             which_heap = HEAP_NATIVE;
         } else if (base::StartsWith(name, "[stack")) {
             which_heap = HEAP_STACK;
+        } else if (base::StartsWith(name, "[anon:stack_and_tls:")) {
+            which_heap = HEAP_STACK;
         } else if (base::EndsWith(name, ".so")) {
             which_heap = HEAP_SO;
             is_swappable = true;
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index a3d4798..71e3860 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -7,7 +7,7 @@
 yro@google.com
 
 # Settings UI
-per-file settings_enums.proto=zhfan@google.com
+per-file settings_enums.proto=tmfang@google.com
 
 # Frameworks
 ogunwale@google.com
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index c023438..3323095 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2401,4 +2401,11 @@
     // OS: Q
     // Note: Gear icon is shown next to gesture navigation preference and opens sensitivity dialog
     SETTINGS_GESTURE_NAV_BACK_SENSITIVITY_DLG = 1748;
+
+    // ---- End Q Constants, all Q constants go above this line ----
+    // OPEN: Settings > Network & Internet > Wi-Fi > Click new network
+    // CATEGORY: SETTINGS
+    // OS: R
+    SETTINGS_WIFI_CONFIGURE_NETWORK = 1800;
+
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f48783f..6a20484 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -727,7 +727,7 @@
          <p>Protection level: dangerous
 
          <p> This is a hard restricted permission which cannot be held by an app until
-         the installer on record did not whitelist the permission. For more details see
+         the installer on record whitelists the permission. For more details see
          {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
     -->
     <permission android:name="android.permission.SEND_SMS"
@@ -741,7 +741,7 @@
          <p>Protection level: dangerous
 
          <p> This is a hard restricted permission which cannot be held by an app until
-         the installer on record did not whitelist the permission. For more details see
+         the installer on record whitelists the permission. For more details see
          {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
     -->
     <permission android:name="android.permission.RECEIVE_SMS"
@@ -755,7 +755,7 @@
          <p>Protection level: dangerous
 
          <p> This is a hard restricted permission which cannot be held by an app until
-         the installer on record did not whitelist the permission. For more details see
+         the installer on record whitelists the permission. For more details see
          {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
     -->
     <permission android:name="android.permission.READ_SMS"
@@ -769,7 +769,7 @@
          <p>Protection level: dangerous
 
          <p> This is a hard restricted permission which cannot be held by an app until
-         the installer on record did not whitelist the permission. For more details see
+         the installer on record whitelists the permission. For more details see
          {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
     -->
     <permission android:name="android.permission.RECEIVE_WAP_PUSH"
@@ -783,7 +783,7 @@
          <p>Protection level: dangerous
 
          <p> This is a hard restricted permission which cannot be held by an app until
-         the installer on record did not whitelist the permission. For more details see
+         the installer on record whitelists the permission. For more details see
          {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
     -->
     <permission android:name="android.permission.RECEIVE_MMS"
@@ -806,7 +806,7 @@
          <p>Protection level: dangerous
 
          <p> This is a hard restricted permission which cannot be held by an app until
-         the installer on record did not whitelist the permission. For more details see
+         the installer on record whitelists the permission. For more details see
          {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
 
          @hide Pending API council approval -->
@@ -852,7 +852,7 @@
       targetSdkVersion}</a> is 4 or higher.
 
       <p> This is a soft restricted permission which cannot be held by an app it its
-      full form until the installer on record did not whitelist the permission.
+      full form until the installer on record whitelists the permission.
       Specifically, if the permission is whitelisted the holder app can access
       external storage and the visual and aural media collections while if the
       permission is not whitelisted the holder app can only access to the visual
@@ -951,7 +951,7 @@
          <p>Protection level: dangerous
 
          <p> This is a hard restricted permission which cannot be held by an app until
-         the installer on record did not whitelist the permission. For more details see
+         the installer on record whitelists the permission. For more details see
          {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
     -->
     <permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
@@ -997,7 +997,7 @@
          <p>Protection level: dangerous
 
          <p> This is a hard restricted permission which cannot be held by an app until
-         the installer on record did not whitelist the permission. For more details see
+         the installer on record whitelists the permission. For more details see
          {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
     -->
     <permission android:name="android.permission.READ_CALL_LOG"
@@ -1021,7 +1021,7 @@
          <p>Protection level: dangerous
 
          <p> This is a hard restricted permission which cannot be held by an app until
-         the installer on record did not whitelist the permission. For more details see
+         the installer on record whitelists the permission. For more details see
          {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
     -->
     <permission android:name="android.permission.WRITE_CALL_LOG"
@@ -1037,7 +1037,7 @@
          <p>Protection level: dangerous
 
          <p> This is a hard restricted permission which cannot be held by an app until
-         the installer on record did not whitelist the permission. For more details see
+         the installer on record whitelists the permission. For more details see
          {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
 
          @deprecated Applications should use {@link android.telecom.CallRedirectionService} instead
@@ -4599,6 +4599,11 @@
     <permission android:name="android.permission.MONITOR_INPUT"
                 android:protectionLevel="signature" />
 
+    <!-- Allows query of any normal app on the device, regardless of manifest declarations. -->
+    <permission android:name="android.permission.QUERY_ALL_PACKAGES"
+                android:protectionLevel="normal" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
@@ -4610,6 +4615,7 @@
                  android:supportsRtl="true"
                  android:theme="@style/Theme.DeviceDefault.Light.DarkActionBar"
                  android:defaultToDeviceProtectedStorage="true"
+                 android:forceQueryable="true"
                  android:directBootAware="true">
         <activity android:name="com.android.internal.app.ChooserActivity"
                 android:theme="@style/Theme.DeviceDefault.Resolver"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index be6cdcf..3ea8a77 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1743,6 +1743,13 @@
                  - {@code true} for apps with targetSdkVersion < 29.
              -->
         <attr name="requestLegacyExternalStorage" format="boolean" />
+
+        <!-- If {@code true} this app declares that it should be visible to all other apps on
+             device, regardless of what they declare via the {@code queries} tags in their
+             manifest.
+
+             The default value is {@code false}. -->
+        <attr name="forceQueryable" format="boolean" />
     </declare-styleable>
     <!-- The <code>permission</code> tag declares a security permission that can be
          used to control access from other packages to specific components or
@@ -1977,6 +1984,12 @@
         <attr name="name" />
     </declare-styleable>
 
+    <declare-styleable name="AndroidManifestQueries" parent="AndroidManifest" />
+    <declare-styleable name="AndroidManifestQueriesPackage" parent="AndroidManifestQueries">
+        <attr name="name" />
+    </declare-styleable>
+    <declare-styleable name="AndroidManifestQueriesIntent" parent="AndroidManifestQueries" />
+
 
     <!-- The <code>static-library</code> tag declares that this apk is providing itself
        as a static shared library for other applications to use. Any app can declare such
@@ -2477,6 +2490,7 @@
             <!-- High dynamic range color mode. -->
             <enum name="hdr" value="2" />
         </attr>
+        <attr name="forceQueryable" format="boolean" />
     </declare-styleable>
 
     <!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3b12753..b34c422 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1700,6 +1700,17 @@
         <!-- Add packages here -->
     </string-array>
 
+    <!-- The set of system packages on device that are queryable regardless of the contents of their
+         manifest. -->
+    <string-array name="config_forceQueryablePackages" translatable="false">
+        <item>com.android.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. -->
+    <bool name="config_forceSystemPackagesQueryable">false</bool>
+
     <!-- Component name of the default wallpaper. This will be ImageWallpaper if not
          specified -->
     <string name="default_wallpaper_component" translatable="false">@null</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 913001c..40f20ab 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -187,6 +187,9 @@
     <string name="notification_channel_wfc">Wi-Fi calling</string>
     <!-- Telephony notification channel name for a channel containing SIM notifications -->
     <string name="notification_channel_sim">SIM status</string>
+    <!-- Telephony notification channel name for a channel containing high priority SIM notifications -->
+    <string name="notification_channel_sim_high_prio">High priority SIM status</string>
+
 
     <!-- Displayed to tell the user that peer changed TTY mode -->
     <string name="peerTtyModeFull">Peer requested TTY Mode FULL</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d91bbd0..ac32c95 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -653,6 +653,7 @@
   <java-symbol type="string" name="notification_channel_voice_mail" />
   <java-symbol type="string" name="notification_channel_wfc" />
   <java-symbol type="string" name="notification_channel_sim" />
+  <java-symbol type="string" name="notification_channel_sim_high_prio" />
   <java-symbol type="string" name="SetupCallDefault" />
   <java-symbol type="string" name="accept" />
   <java-symbol type="string" name="activity_chooser_view_see_all" />
@@ -786,6 +787,8 @@
   <java-symbol type="string" name="widget_default_class_name" />
   <java-symbol type="string" name="emergency_calls_only" />
   <java-symbol type="array" name="config_ephemeralResolverPackage" />
+  <java-symbol type="array" name="config_forceQueryablePackages" />
+  <java-symbol type="bool" name="config_forceSystemPackagesQueryable" />
   <java-symbol type="string" name="eventTypeAnniversary" />
   <java-symbol type="string" name="eventTypeBirthday" />
   <java-symbol type="string" name="eventTypeCustom" />
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
index b2254c5..eadf226 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
@@ -17,12 +17,10 @@
 
 import static org.junit.Assert.*;
 
-import android.hardware.broadcastradio.V2_0.ProgramInfo;
 import android.hardware.broadcastradio.V2_0.ProgramListChunk;
 import android.hardware.radio.ProgramList;
 import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
-import android.hardware.radio.RadioMetadata;
 import android.test.suitebuilder.annotation.MediumTest;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -30,7 +28,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -45,55 +42,58 @@
 
     private final ProgramSelector.Identifier mAmFmIdentifier =
             new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, 88500);
-    private final RadioManager.ProgramInfo mAmFmInfo = makeProgramInfo(
+    private final RadioManager.ProgramInfo mAmFmInfo = TestUtils.makeProgramInfo(
             ProgramSelector.PROGRAM_TYPE_FM, mAmFmIdentifier, 0);
 
     private final ProgramSelector.Identifier mRdsIdentifier =
             new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_RDS_PI, 15019);
-    private final RadioManager.ProgramInfo mRdsInfo = makeProgramInfo(
+    private final RadioManager.ProgramInfo mRdsInfo = TestUtils.makeProgramInfo(
             ProgramSelector.PROGRAM_TYPE_FM, mRdsIdentifier, 0);
 
     private final ProgramSelector.Identifier mDabEnsembleIdentifier =
             new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, 1337);
-    private final RadioManager.ProgramInfo mDabEnsembleInfo = makeProgramInfo(
+    private final RadioManager.ProgramInfo mDabEnsembleInfo = TestUtils.makeProgramInfo(
             ProgramSelector.PROGRAM_TYPE_DAB, mDabEnsembleIdentifier, 0);
 
     private final ProgramSelector.Identifier mVendorCustomIdentifier =
             new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_VENDOR_START, 9001);
-    private final RadioManager.ProgramInfo mVendorCustomInfo = makeProgramInfo(
+    private final RadioManager.ProgramInfo mVendorCustomInfo = TestUtils.makeProgramInfo(
             ProgramSelector.PROGRAM_TYPE_VENDOR_START, mVendorCustomIdentifier, 0);
 
     // HAL-side ProgramInfoCache containing all of the above ProgramInfos.
-    private final ProgramInfoCache mAllProgramInfos = new ProgramInfoCache(null, mAmFmInfo,
+    private final ProgramInfoCache mAllProgramInfos = new ProgramInfoCache(null, true, mAmFmInfo,
             mRdsInfo, mDabEnsembleInfo, mVendorCustomInfo);
 
     @Test
     public void testUpdateFromHal() {
-        // First test a purging chunk.
-        ProgramInfoCache cache = new ProgramInfoCache(null, mAmFmInfo);
+        // First test updating an incomplete cache with a purging, complete chunk.
+        ProgramInfoCache cache = new ProgramInfoCache(null, false, mAmFmInfo);
         ProgramListChunk chunk = new ProgramListChunk();
         chunk.purge = true;
-        chunk.modified.add(programInfoToHal(mRdsInfo));
-        chunk.modified.add(programInfoToHal(mDabEnsembleInfo));
-        cache.updateFromHalProgramListChunk(chunk);
         chunk.complete = true;
+        chunk.modified.add(TestUtils.programInfoToHal(mRdsInfo));
+        chunk.modified.add(TestUtils.programInfoToHal(mDabEnsembleInfo));
+        cache.updateFromHalProgramListChunk(chunk);
         assertTrue(cache.programInfosAreExactly(mRdsInfo, mDabEnsembleInfo));
+        assertTrue(cache.isComplete());
 
-        // Then test a non-purging chunk.
+        // Then test a non-purging, incomplete chunk.
         chunk.purge = false;
+        chunk.complete = false;
         chunk.modified.clear();
-        RadioManager.ProgramInfo updatedRdsInfo = makeProgramInfo(ProgramSelector.PROGRAM_TYPE_FM,
-                mRdsIdentifier, 1);
-        chunk.modified.add(programInfoToHal(updatedRdsInfo));
-        chunk.modified.add(programInfoToHal(mVendorCustomInfo));
+        RadioManager.ProgramInfo updatedRdsInfo = TestUtils.makeProgramInfo(
+                ProgramSelector.PROGRAM_TYPE_FM, mRdsIdentifier, 1);
+        chunk.modified.add(TestUtils.programInfoToHal(updatedRdsInfo));
+        chunk.modified.add(TestUtils.programInfoToHal(mVendorCustomInfo));
         chunk.removed.add(Convert.programIdentifierToHal(mDabEnsembleIdentifier));
         cache.updateFromHalProgramListChunk(chunk);
         assertTrue(cache.programInfosAreExactly(updatedRdsInfo, mVendorCustomInfo));
+        assertFalse(cache.isComplete());
     }
 
     @Test
     public void testNullFilter() {
-        ProgramInfoCache cache = new ProgramInfoCache(null);
+        ProgramInfoCache cache = new ProgramInfoCache(null, true);
         cache.filterAndUpdateFrom(mAllProgramInfos, false);
         assertTrue(cache.programInfosAreExactly(mAmFmInfo, mRdsInfo, mDabEnsembleInfo,
                   mVendorCustomInfo));
@@ -140,11 +140,11 @@
 
     @Test
     public void testPurgeUpdateChunks() {
-        ProgramInfoCache cache = new ProgramInfoCache(null, mAmFmInfo);
+        ProgramInfoCache cache = new ProgramInfoCache(null, false, mAmFmInfo);
         List<ProgramList.Chunk> chunks =
                 cache.filterAndUpdateFromInternal(mAllProgramInfos, true, 3, 3);
         assertEquals(2, chunks.size());
-        verifyChunkListFlags(chunks, true);
+        verifyChunkListFlags(chunks, true, true);
         verifyChunkListModified(chunks, 3, mAmFmInfo, mRdsInfo, mDabEnsembleInfo,
                 mVendorCustomInfo);
         verifyChunkListRemoved(chunks, 0);
@@ -154,23 +154,26 @@
     public void testDeltaUpdateChunksModificationsIncluded() {
         // Create a cache with a filter that allows modifications, and set its contents to
         // mAmFmInfo, mRdsInfo, mDabEnsembleInfo, and mVendorCustomInfo.
-        ProgramInfoCache cache = new ProgramInfoCache(null, mAmFmInfo, mRdsInfo, mDabEnsembleInfo,
-                mVendorCustomInfo);
+        ProgramInfoCache cache = new ProgramInfoCache(null, true, mAmFmInfo, mRdsInfo,
+                mDabEnsembleInfo, mVendorCustomInfo);
 
         // Create a HAL cache that:
+        // - Is complete.
         // - Retains mAmFmInfo.
         // - Replaces mRdsInfo with updatedRdsInfo.
         // - Drops mDabEnsembleInfo and mVendorCustomInfo.
         // - Introduces a new SXM info.
-        RadioManager.ProgramInfo updatedRdsInfo = makeProgramInfo(ProgramSelector.PROGRAM_TYPE_FM,
-                mRdsIdentifier, 1);
-        RadioManager.ProgramInfo newSxmInfo = makeProgramInfo(ProgramSelector.PROGRAM_TYPE_SXM,
+        RadioManager.ProgramInfo updatedRdsInfo = TestUtils.makeProgramInfo(
+                ProgramSelector.PROGRAM_TYPE_FM, mRdsIdentifier, 1);
+        RadioManager.ProgramInfo newSxmInfo = TestUtils.makeProgramInfo(
+                ProgramSelector.PROGRAM_TYPE_SXM,
                 new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL, 12345),
                 0);
-        ProgramInfoCache halCache = new ProgramInfoCache(null, mAmFmInfo, updatedRdsInfo,
+        ProgramInfoCache halCache = new ProgramInfoCache(null, true, mAmFmInfo, updatedRdsInfo,
                 newSxmInfo);
 
         // Update the cache and verify:
+        // - The final chunk's complete flag is set.
         // - mAmFmInfo is retained and not reported in the chunks.
         // - updatedRdsInfo should appear as an update to mRdsInfo.
         // - newSxmInfo should appear as a new entry.
@@ -178,7 +181,7 @@
         List<ProgramList.Chunk> chunks = cache.filterAndUpdateFromInternal(halCache, false, 5, 1);
         assertTrue(cache.programInfosAreExactly(mAmFmInfo, updatedRdsInfo, newSxmInfo));
         assertEquals(2, chunks.size());
-        verifyChunkListFlags(chunks, false);
+        verifyChunkListFlags(chunks, false, true);
         verifyChunkListModified(chunks, 5, updatedRdsInfo, newSxmInfo);
         verifyChunkListRemoved(chunks, 1, mDabEnsembleIdentifier, mVendorCustomIdentifier);
     }
@@ -188,63 +191,50 @@
         // Create a cache with a filter that excludes modifications, and set its contents to
         // mAmFmInfo, mRdsInfo, mDabEnsembleInfo, and mVendorCustomInfo.
         ProgramInfoCache cache = new ProgramInfoCache(new ProgramList.Filter(new HashSet<Integer>(),
-                new HashSet<ProgramSelector.Identifier>(), true, true), mAmFmInfo, mRdsInfo,
+                new HashSet<ProgramSelector.Identifier>(), true, true), true, mAmFmInfo, mRdsInfo,
                 mDabEnsembleInfo, mVendorCustomInfo);
 
         // Create a HAL cache that:
+        // - Is incomplete.
         // - Retains mAmFmInfo.
         // - Replaces mRdsInfo with updatedRdsInfo.
         // - Drops mDabEnsembleInfo and mVendorCustomInfo.
         // - Introduces a new SXM info.
-        RadioManager.ProgramInfo updatedRdsInfo = makeProgramInfo(ProgramSelector.PROGRAM_TYPE_FM,
-                mRdsIdentifier, 1);
-        RadioManager.ProgramInfo newSxmInfo = makeProgramInfo(ProgramSelector.PROGRAM_TYPE_SXM,
+        RadioManager.ProgramInfo updatedRdsInfo = TestUtils.makeProgramInfo(
+                ProgramSelector.PROGRAM_TYPE_FM, mRdsIdentifier, 1);
+        RadioManager.ProgramInfo newSxmInfo = TestUtils.makeProgramInfo(
+                ProgramSelector.PROGRAM_TYPE_SXM,
                 new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL, 12345),
                 0);
-        ProgramInfoCache halCache = new ProgramInfoCache(null, mAmFmInfo, updatedRdsInfo,
+        ProgramInfoCache halCache = new ProgramInfoCache(null, false, mAmFmInfo, updatedRdsInfo,
                 newSxmInfo);
 
         // Update the cache and verify:
+        // - All complete flags are false.
         // - mAmFmInfo and mRdsInfo are retained and not reported in the chunks.
         // - newSxmInfo should appear as a new entry.
         // - mDabEnsembleInfo and mVendorCustomInfo should be reported as removed.
         List<ProgramList.Chunk> chunks = cache.filterAndUpdateFromInternal(halCache, false, 5, 1);
         assertTrue(cache.programInfosAreExactly(mAmFmInfo, mRdsInfo, newSxmInfo));
         assertEquals(2, chunks.size());
-        verifyChunkListFlags(chunks, false);
+        verifyChunkListFlags(chunks, false, false);
         verifyChunkListModified(chunks, 5, newSxmInfo);
         verifyChunkListRemoved(chunks, 1, mDabEnsembleIdentifier, mVendorCustomIdentifier);
     }
 
-    private static RadioManager.ProgramInfo makeProgramInfo(int programType,
-            ProgramSelector.Identifier identifier, int signalQuality) {
-        // Note: If you set new fields, check if programInfoToHal() needs to be updated as well.
-        return new RadioManager.ProgramInfo(new ProgramSelector(programType, identifier, null,
-                null), null, null, null, 0, signalQuality, new RadioMetadata.Builder().build(),
-                new HashMap<String, String>());
-    }
-
-    private static ProgramInfo programInfoToHal(RadioManager.ProgramInfo info) {
-        // Note that because Convert does not by design provide functions for all conversions, this
-        // function only copies fields that are set by makeProgramInfo().
-        ProgramInfo hwInfo = new ProgramInfo();
-        hwInfo.selector = Convert.programSelectorToHal(info.getSelector());
-        hwInfo.signalQuality = info.getSignalStrength();
-        return hwInfo;
-    }
-
     // Verifies that:
     // - The first chunk's purge flag matches expectPurge.
-    // - The last chunk's complete flag is set.
+    // - The last chunk's complete flag matches expectComplete.
     // - All other flags are false.
-    private static void verifyChunkListFlags(List<ProgramList.Chunk> chunks, boolean expectPurge) {
+    private static void verifyChunkListFlags(List<ProgramList.Chunk> chunks, boolean expectPurge,
+            boolean expectComplete) {
         if (chunks.isEmpty()) {
             return;
         }
         for (int i = 0; i < chunks.size(); i++) {
             ProgramList.Chunk chunk = chunks.get(i);
             assertEquals(i == 0 && expectPurge, chunk.isPurge());
-            assertEquals(i == chunks.size() - 1, chunk.isComplete());
+            assertEquals(i == chunks.size() - 1 && expectComplete, chunk.isComplete());
         }
     }
 
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
new file mode 100644
index 0000000..f9e3798
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -0,0 +1,353 @@
+/*
+ * 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.broadcastradio.hal2;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
+import android.hardware.broadcastradio.V2_0.ITunerCallback;
+import android.hardware.broadcastradio.V2_0.ITunerSession;
+import android.hardware.broadcastradio.V2_0.ProgramFilter;
+import android.hardware.broadcastradio.V2_0.ProgramListChunk;
+import android.hardware.broadcastradio.V2_0.Result;
+import android.hardware.radio.ProgramList;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+import android.os.RemoteException;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Tests for v2 HAL RadioModule.
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class StartProgramListUpdatesFanoutTest {
+    private static final String TAG = "BroadcastRadioTests.hal2.StartProgramListUpdatesFanout";
+
+    // Mocks
+    @Mock IBroadcastRadio mBroadcastRadioMock;
+    @Mock ITunerSession mHalTunerSessionMock;
+    private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
+
+    // RadioModule under test
+    private RadioModule mRadioModule;
+
+    // Objects created by mRadioModule
+    private ITunerCallback mHalTunerCallback;
+    private TunerSession[] mTunerSessions;
+
+    // Data objects used during tests
+    private final ProgramSelector.Identifier mAmFmIdentifier =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, 88500);
+    private final RadioManager.ProgramInfo mAmFmInfo = TestUtils.makeProgramInfo(
+            ProgramSelector.PROGRAM_TYPE_FM, mAmFmIdentifier, 0);
+    private final RadioManager.ProgramInfo mModifiedAmFmInfo = TestUtils.makeProgramInfo(
+            ProgramSelector.PROGRAM_TYPE_FM, mAmFmIdentifier, 1);
+
+    private final ProgramSelector.Identifier mRdsIdentifier =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_RDS_PI, 15019);
+    private final RadioManager.ProgramInfo mRdsInfo = TestUtils.makeProgramInfo(
+            ProgramSelector.PROGRAM_TYPE_FM, mRdsIdentifier, 0);
+
+    private final ProgramSelector.Identifier mDabEnsembleIdentifier =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, 1337);
+    private final RadioManager.ProgramInfo mDabEnsembleInfo = TestUtils.makeProgramInfo(
+            ProgramSelector.PROGRAM_TYPE_DAB, mDabEnsembleIdentifier, 0);
+
+    @Before
+    public void setup() throws RemoteException {
+        MockitoAnnotations.initMocks(this);
+
+        mRadioModule = new RadioModule(mBroadcastRadioMock, new RadioManager.ModuleProperties(0, "",
+                  0, "", "", "", "", 0, 0, false, false, null, false, new int[] {}, new int[] {},
+                  null, null));
+
+        doAnswer((Answer) invocation -> {
+            mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
+            IBroadcastRadio.openSessionCallback cb = (IBroadcastRadio.openSessionCallback)
+                    invocation.getArguments()[1];
+            cb.onValues(Result.OK, mHalTunerSessionMock);
+            return null;
+        }).when(mBroadcastRadioMock).openSession(any(), any());
+        when(mHalTunerSessionMock.startProgramListUpdates(any())).thenReturn(Result.OK);
+    }
+
+    @Test
+    public void testFanout() throws RemoteException {
+        // Open 3 clients that will all use the same filter, and start updates on two of them for
+        // now. The HAL TunerSession should only see 1 filter update.
+        openAidlClients(3);
+        ProgramList.Filter aidlFilter = new ProgramList.Filter(new HashSet<Integer>(),
+                new HashSet<ProgramSelector.Identifier>(), true, false);
+        ProgramFilter halFilter = Convert.programFilterToHal(aidlFilter);
+        for (int i = 0; i < 2; i++) {
+            mTunerSessions[i].startProgramListUpdates(aidlFilter);
+        }
+        verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(halFilter);
+
+        // Initiate a program list update from the HAL side and verify both connected AIDL clients
+        // receive the update.
+        updateHalProgramInfo(true, Arrays.asList(mAmFmInfo, mRdsInfo), null);
+        for (int i = 0; i < 2; i++) {
+            verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[i], true, Arrays.asList(
+                    mAmFmInfo, mRdsInfo), null);
+        }
+
+        // Repeat with a non-purging update.
+        updateHalProgramInfo(false, Arrays.asList(mModifiedAmFmInfo),
+                Arrays.asList(mRdsIdentifier));
+        for (int i = 0; i < 2; i++) {
+            verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[i], false,
+                    Arrays.asList(mModifiedAmFmInfo), Arrays.asList(mRdsIdentifier));
+        }
+
+        // Now start updates on the 3rd client. Verify the HAL function has not been called again
+        // and client receives the appropriate update.
+        mTunerSessions[2].startProgramListUpdates(aidlFilter);
+        verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(any());
+        verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[2], true,
+                Arrays.asList(mModifiedAmFmInfo), null);
+    }
+
+    @Test
+    public void testFiltering() throws RemoteException {
+        // Open 4 clients that will use the following filters:
+        // [0]: ID mRdsIdentifier, modifications excluded
+        // [1]: No categories, modifications excluded
+        // [2]: Type IDENTIFIER_TYPE_AMFM_FREQUENCY, modifications excluded
+        // [3]: Type IDENTIFIER_TYPE_AMFM_FREQUENCY, modifications included
+        openAidlClients(4);
+        ProgramList.Filter idFilter = new ProgramList.Filter(new HashSet<Integer>(),
+                new HashSet<ProgramSelector.Identifier>(Arrays.asList(mRdsIdentifier)), true, true);
+        ProgramList.Filter categoryFilter = new ProgramList.Filter(new HashSet<Integer>(),
+                new HashSet<ProgramSelector.Identifier>(), false, true);
+        ProgramList.Filter typeFilterWithoutModifications = new ProgramList.Filter(
+                new HashSet<Integer>(Arrays.asList(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)),
+                new HashSet<ProgramSelector.Identifier>(), true, true);
+        ProgramList.Filter typeFilterWithModifications = new ProgramList.Filter(
+                new HashSet<Integer>(Arrays.asList(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)),
+                new HashSet<ProgramSelector.Identifier>(), true, false);
+
+        // Start updates on the clients in order. The HAL filter should get updated after each
+        // client except [2].
+        mTunerSessions[0].startProgramListUpdates(idFilter);
+        ProgramFilter halFilter = Convert.programFilterToHal(idFilter);
+        verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(halFilter);
+
+        mTunerSessions[1].startProgramListUpdates(categoryFilter);
+        halFilter.identifiers.clear();
+        verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(halFilter);
+
+        mTunerSessions[2].startProgramListUpdates(typeFilterWithoutModifications);
+        verify(mHalTunerSessionMock, times(2)).startProgramListUpdates(any());
+
+        mTunerSessions[3].startProgramListUpdates(typeFilterWithModifications);
+        halFilter.excludeModifications = false;
+        verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(halFilter);
+
+        // Adding mRdsInfo should update clients [0] and [1].
+        updateHalProgramInfo(false, Arrays.asList(mRdsInfo), null);
+        verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[0], false, Arrays.asList(mRdsInfo),
+                null);
+        verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[1], false, Arrays.asList(mRdsInfo),
+                null);
+
+        // Adding mAmFmInfo should update clients [1], [2], and [3].
+        updateHalProgramInfo(false, Arrays.asList(mAmFmInfo), null);
+        verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[1], false, Arrays.asList(mAmFmInfo),
+                null);
+        verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[2], false, Arrays.asList(mAmFmInfo),
+                null);
+        verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[3], false, Arrays.asList(mAmFmInfo),
+                null);
+
+        // Modifying mAmFmInfo to mModifiedAmFmInfo should update only [3].
+        updateHalProgramInfo(false, Arrays.asList(mModifiedAmFmInfo), null);
+        verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[3], false,
+                Arrays.asList(mModifiedAmFmInfo), null);
+
+        // Adding mDabEnsembleInfo should not update any client.
+        updateHalProgramInfo(false, Arrays.asList(mDabEnsembleInfo), null);
+        verify(mAidlTunerCallbackMocks[0], times(1)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[1], times(2)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[2], times(1)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[3], times(2)).onProgramListUpdated(any());
+    }
+
+    @Test
+    public void testClientClosing() throws RemoteException {
+        // Open 2 clients that use different filters that are both sensitive to mAmFmIdentifier.
+        openAidlClients(2);
+        ProgramList.Filter idFilter = new ProgramList.Filter(new HashSet<Integer>(),
+                new HashSet<ProgramSelector.Identifier>(Arrays.asList(mAmFmIdentifier)), true,
+                false);
+        ProgramList.Filter typeFilter = new ProgramList.Filter(
+                new HashSet<Integer>(Arrays.asList(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)),
+                new HashSet<ProgramSelector.Identifier>(), true, false);
+
+        // Start updates on the clients, and verify the HAL filter is updated after each one.
+        mTunerSessions[0].startProgramListUpdates(idFilter);
+        ProgramFilter halFilter = Convert.programFilterToHal(idFilter);
+        verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(halFilter);
+
+        mTunerSessions[1].startProgramListUpdates(typeFilter);
+        halFilter.identifiers.clear();
+        verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(halFilter);
+
+        // Update the HAL with mAmFmInfo, and verify both clients are updated.
+        updateHalProgramInfo(true, Arrays.asList(mAmFmInfo), null);
+        verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[0], true, Arrays.asList(mAmFmInfo),
+                null);
+        verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[1], true, Arrays.asList(mAmFmInfo),
+                null);
+
+        // Stop updates on the first client and verify the HAL filter is updated.
+        mTunerSessions[0].stopProgramListUpdates();
+        verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(Convert.programFilterToHal(
+                typeFilter));
+
+        // Update the HAL with mModifiedAmFmInfo, and verify only the remaining client is updated.
+        updateHalProgramInfo(true, Arrays.asList(mModifiedAmFmInfo), null);
+        verify(mAidlTunerCallbackMocks[0], times(1)).onProgramListUpdated(any());
+        verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[1], true,
+                Arrays.asList(mModifiedAmFmInfo), null);
+
+        // Close the other client without explicitly stopping updates, and verify HAL updates are
+        // stopped as well.
+        mTunerSessions[1].close();
+        verify(mHalTunerSessionMock).stopProgramListUpdates();
+    }
+
+    private void openAidlClients(int numClients) throws RemoteException {
+        mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients];
+        mTunerSessions = new TunerSession[numClients];
+        for (int i = 0; i < numClients; i++) {
+            mAidlTunerCallbackMocks[i] = mock(android.hardware.radio.ITunerCallback.class);
+            mTunerSessions[i] = mRadioModule.openSession(mAidlTunerCallbackMocks[i]);
+        }
+    }
+
+    private void updateHalProgramInfo(boolean purge, List<RadioManager.ProgramInfo> modified,
+            List<ProgramSelector.Identifier> removed) throws RemoteException {
+        ProgramListChunk programListChunk = new ProgramListChunk();
+        programListChunk.purge = purge;
+        programListChunk.complete = true;
+        if (modified != null) {
+            for (RadioManager.ProgramInfo mod : modified) {
+                programListChunk.modified.add(TestUtils.programInfoToHal(mod));
+            }
+        }
+        if (removed != null) {
+            for (ProgramSelector.Identifier id : removed) {
+                programListChunk.removed.add(Convert.programIdentifierToHal(id));
+            }
+        }
+        mHalTunerCallback.onProgramListUpdated(programListChunk);
+    }
+
+    private void verifyAidlClientReceivedChunk(android.hardware.radio.ITunerCallback clientMock,
+            boolean purge, List<RadioManager.ProgramInfo> modified,
+            List<ProgramSelector.Identifier> removed) throws RemoteException {
+        HashSet<RadioManager.ProgramInfo> modifiedSet = new HashSet<>();
+        if (modified != null) {
+            modifiedSet.addAll(modified);
+        }
+        HashSet<ProgramSelector.Identifier> removedSet = new HashSet<>();
+        if (removed != null) {
+            removedSet.addAll(removed);
+        }
+        ProgramList.Chunk expectedChunk = new ProgramList.Chunk(purge, true, modifiedSet,
+                removedSet);
+        verify(clientMock).onProgramListUpdated(argThat(new ChunkMatcher(expectedChunk)));
+    }
+
+    // TODO(b/130750904): Remove this class and replace "argThat(new ChunkMatcher(chunk))" with
+    // "eq(chunk)".
+    //
+    // Ideally, this class wouldn't exist, but currently RadioManager.ProgramInfo#hashCode() can
+    // return different values for objects that satisfy ProgramInfo#equals(). As a short term
+    // workaround, this class performs the O(N^2) comparison between the Chunks' mModified sets.
+    //
+    // To test if ProgramInfo#hashCode() has been fixed, remove commenting from
+    // testProgramInfoHashCode() below.
+    private class ChunkMatcher implements ArgumentMatcher<ProgramList.Chunk> {
+        private final ProgramList.Chunk mExpected;
+
+        ChunkMatcher(ProgramList.Chunk expected) {
+            mExpected = expected;
+        }
+
+        @Override
+        public boolean matches(ProgramList.Chunk actual) {
+            if ((mExpected.isPurge() != actual.isPurge())
+                    || (mExpected.isComplete() != actual.isComplete())
+                    || (!mExpected.getRemoved().equals(actual.getRemoved()))) {
+                return false;
+            }
+            Set<RadioManager.ProgramInfo> expectedModified = mExpected.getModified();
+            Set<RadioManager.ProgramInfo> actualModified = new HashSet<>(actual.getModified());
+            if (expectedModified.size() != actualModified.size()) {
+                return false;
+            }
+            for (RadioManager.ProgramInfo expectedInfo : expectedModified) {
+                boolean found = false;
+                for (RadioManager.ProgramInfo actualInfo : actualModified) {
+                    if (expectedInfo.equals(actualInfo)) {
+                        found = true;
+                        actualModified.remove(actualInfo);
+                        break;
+                    }
+                }
+                if (!found) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    // @Test
+    // public void testProgramInfoHashCode() {
+    //     RadioManager.ProgramInfo info1 = TestUtils.makeProgramInfo(
+    //         ProgramSelector.PROGRAM_TYPE_FM, mAmFmIdentifier, 0);
+    //     RadioManager.ProgramInfo info2 = TestUtils.makeProgramInfo(
+    //         ProgramSelector.PROGRAM_TYPE_FM, mAmFmIdentifier, 0);
+    //     assertEquals(info1, info2);
+    //     assertEquals(info1.hashCode(), info2.hashCode());
+    // }
+}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java
new file mode 100644
index 0000000..4944803
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java
@@ -0,0 +1,42 @@
+/*
+ * 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.broadcastradio.hal2;
+
+import android.hardware.broadcastradio.V2_0.ProgramInfo;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioMetadata;
+
+import java.util.HashMap;
+
+final class TestUtils {
+    static RadioManager.ProgramInfo makeProgramInfo(int programType,
+            ProgramSelector.Identifier identifier, int signalQuality) {
+        // Note: If you set new fields, check if programInfoToHal() needs to be updated as well.
+        return new RadioManager.ProgramInfo(new ProgramSelector(programType, identifier, null,
+                null), null, null, null, 0, signalQuality, new RadioMetadata.Builder().build(),
+                new HashMap<String, String>());
+    }
+
+    static ProgramInfo programInfoToHal(RadioManager.ProgramInfo info) {
+        // Note that because Convert does not by design provide functions for all conversions, this
+        // function only copies fields that are set by makeProgramInfo().
+        ProgramInfo hwInfo = new ProgramInfo();
+        hwInfo.selector = Convert.programSelectorToHal(info.getSelector());
+        hwInfo.signalQuality = info.getSignalStrength();
+        return hwInfo;
+    }
+}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index adaae5c..682416c 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -132,6 +132,8 @@
 
     public void sendGesture(int sequence, ParceledListSlice gestureSteps) {}
 
+    public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {}
+
     public boolean isFingerprintGestureDetectionAvailable() {
         return false;
     }
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
index 008085e..a935737 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java
@@ -68,12 +68,7 @@
     private WakeLockInfo createWakeLockInfo(String name, int activeCount, long totalTime) {
         WakeLockInfo info = new WakeLockInfo();
         info.name = name;
-        info.pid = 1;
         info.activeCount = activeCount;
-        info.isActive = true;
-        info.activeSince = 0;
-        info.lastChange = 0;
-        info.maxTime = 0;
         info.totalTime = totalTime;
         return info;
     }
@@ -89,7 +84,7 @@
                                                         byte[] buffer, WakeLockInfo[] wlStats) {
         mReader.updateVersion(staleStats);
         mReader.parseProcWakelocks(buffer, buffer.length, true, staleStats);
-        mReader.getNativeWakelockStats(wlStats, staleStats);
+        mReader.updateWakelockStats(wlStats, staleStats);
         return mReader.removeOldStats(staleStats);
     }
 
@@ -101,7 +96,7 @@
         mReader = new KernelWakelockReader();
     }
 
-// ------------------------- Kernel Wakelock Stats Test ------------------------
+// ------------------------- Legacy Wakelock Stats Test ------------------------
     @SmallTest
     public void testParseEmptyFile() throws Exception {
         KernelWakelockStats staleStats = mReader.parseProcWakelocks(new byte[0], 0, true,
@@ -196,10 +191,10 @@
         assertFalse(staleStats.containsKey("Fakelock"));
     }
 
-// -------------------- Native (SystemSuspend) Wakelock Stats Test -------------------
+// -------------------- SystemSuspend Wakelock Stats Test -------------------
     @SmallTest
     public void testEmptyWakeLockInfoList() {
-        KernelWakelockStats staleStats = mReader.getNativeWakelockStats(new WakeLockInfo[0],
+        KernelWakelockStats staleStats = mReader.updateWakelockStats(new WakeLockInfo[0],
                 new KernelWakelockStats());
 
         assertTrue(staleStats.isEmpty());
@@ -208,9 +203,9 @@
     @SmallTest
     public void testOneWakeLockInfo() {
         WakeLockInfo[] wlStats = new WakeLockInfo[1];
-        wlStats[0] = createWakeLockInfo("WakeLock", 20, 10000);
+        wlStats[0] = createWakeLockInfo("WakeLock", 20, 1000);   // Milliseconds
 
-        KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats,
+        KernelWakelockStats staleStats = mReader.updateWakelockStats(wlStats,
                 new KernelWakelockStats());
 
         assertEquals(1, staleStats.size());
@@ -219,16 +214,16 @@
 
         KernelWakelockStats.Entry entry = staleStats.get("WakeLock");
         assertEquals(20, entry.mCount);
-        assertEquals(10000, entry.mTotalTime);
+        assertEquals(1000 * 1000, entry.mTotalTime);   // Microseconds
     }
 
     @SmallTest
     public void testTwoWakeLockInfos() {
         WakeLockInfo[] wlStats = new WakeLockInfo[2];
-        wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000);
-        wlStats[1] = createWakeLockInfo("WakeLock2", 20, 2000);
+        wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds
+        wlStats[1] = createWakeLockInfo("WakeLock2", 20, 2000); // Milliseconds
 
-        KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats,
+        KernelWakelockStats staleStats = mReader.updateWakelockStats(wlStats,
                 new KernelWakelockStats());
 
         assertEquals(2, staleStats.size());
@@ -238,17 +233,17 @@
 
         KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1");
         assertEquals(10, entry1.mCount);
-        assertEquals(1000, entry1.mTotalTime);
+        assertEquals(1000 * 1000, entry1.mTotalTime); // Microseconds
 
         KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2");
         assertEquals(20, entry2.mCount);
-        assertEquals(2000, entry2.mTotalTime);
+        assertEquals(2000 * 1000, entry2.mTotalTime); // Microseconds
     }
 
     @SmallTest
     public void testWakeLockInfosBecomeStale() {
         WakeLockInfo[] wlStats = new WakeLockInfo[1];
-        wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000);
+        wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds
 
         KernelWakelockStats staleStats = new KernelWakelockStats();
 
@@ -259,9 +254,9 @@
         assertTrue(staleStats.containsKey("WakeLock1"));
         KernelWakelockStats.Entry entry = staleStats.get("WakeLock1");
         assertEquals(10, entry.mCount);
-        assertEquals(1000, entry.mTotalTime);
+        assertEquals(1000 * 1000, entry.mTotalTime);  // Microseconds
 
-        wlStats[0] = createWakeLockInfo("WakeLock2", 20, 2000);
+        wlStats[0] = createWakeLockInfo("WakeLock2", 20, 2000); // Milliseconds
 
         readKernelWakelockStats(staleStats, new byte[0], wlStats);
 
@@ -271,146 +266,6 @@
         assertTrue(staleStats.containsKey("WakeLock2"));
         entry = staleStats.get("WakeLock2");
         assertEquals(20, entry.mCount);
-        assertEquals(2000, entry.mTotalTime);
-    }
-
-// -------------------- Aggregate  Wakelock Stats Tests --------------------
-    @SmallTest
-    public void testAggregateStatsEmpty() throws Exception {
-        KernelWakelockStats staleStats = new KernelWakelockStats();
-
-        byte[] buffer = new byte[0];
-        WakeLockInfo[] wlStats = new WakeLockInfo[0];
-
-        readKernelWakelockStats(staleStats, buffer, wlStats);
-
-        assertTrue(staleStats.isEmpty());
-    }
-
-    @SmallTest
-    public void testAggregateStatsNoNativeWakelocks() throws Exception {
-        KernelWakelockStats staleStats = new KernelWakelockStats();
-
-        byte[] buffer = new ProcFileBuilder()
-                .addLine("Wakelock", 34, 123) // Milliseconds
-                .getBytes();
-        WakeLockInfo[] wlStats = new WakeLockInfo[0];
-
-        readKernelWakelockStats(staleStats, buffer, wlStats);
-
-        assertEquals(1, staleStats.size());
-
-        assertTrue(staleStats.containsKey("Wakelock"));
-
-        KernelWakelockStats.Entry entry = staleStats.get("Wakelock");
-        assertEquals(34, entry.mCount);
-        assertEquals(1000 * 123, entry.mTotalTime);  // Microseconds
-    }
-
-    @SmallTest
-    public void testAggregateStatsNoKernelWakelocks() throws Exception {
-        KernelWakelockStats staleStats = new KernelWakelockStats();
-
-        byte[] buffer = new byte[0];
-        WakeLockInfo[] wlStats = new WakeLockInfo[1];
-        wlStats[0] = createWakeLockInfo("WakeLock", 10, 1000);
-
-        readKernelWakelockStats(staleStats, buffer, wlStats);
-
-        assertEquals(1, staleStats.size());
-
-        assertTrue(staleStats.containsKey("WakeLock"));
-
-        KernelWakelockStats.Entry entry = staleStats.get("WakeLock");
-        assertEquals(10, entry.mCount);
-        assertEquals(1000, entry.mTotalTime);
-    }
-
-    @SmallTest
-    public void testAggregateStatsBothKernelAndNativeWakelocks() throws Exception {
-        KernelWakelockStats staleStats = new KernelWakelockStats();
-
-        byte[] buffer = new ProcFileBuilder()
-                .addLine("WakeLock1", 34, 123)  // Milliseconds
-                .getBytes();
-        WakeLockInfo[] wlStats = new WakeLockInfo[1];
-        wlStats[0] = createWakeLockInfo("WakeLock2", 10, 1000);
-
-        readKernelWakelockStats(staleStats, buffer, wlStats);
-
-        assertEquals(2, staleStats.size());
-
-        assertTrue(staleStats.containsKey("WakeLock1"));
-        KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1");
-        assertEquals(34, entry1.mCount);
-        assertEquals(123 * 1000, entry1.mTotalTime);  // Microseconds
-
-        assertTrue(staleStats.containsKey("WakeLock2"));
-        KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2");
-        assertEquals(10, entry2.mCount);
-        assertEquals(1000, entry2.mTotalTime);
-    }
-
-    @SmallTest
-    public void testAggregateStatsUpdate() throws Exception {
-        KernelWakelockStats staleStats = new KernelWakelockStats();
-
-        byte[] buffer = new ProcFileBuilder()
-                .addLine("WakeLock1", 34, 123)  // Milliseconds
-                .addLine("WakeLock2", 46, 345)  // Milliseconds
-                .getBytes();
-        WakeLockInfo[] wlStats = new WakeLockInfo[2];
-        wlStats[0] = createWakeLockInfo("WakeLock3", 10, 1000);
-        wlStats[1] = createWakeLockInfo("WakeLock4", 20, 2000);
-
-        readKernelWakelockStats(staleStats, buffer, wlStats);
-
-        assertEquals(4, staleStats.size());
-
-        assertTrue(staleStats.containsKey("WakeLock1"));
-        assertTrue(staleStats.containsKey("WakeLock2"));
-        assertTrue(staleStats.containsKey("WakeLock3"));
-        assertTrue(staleStats.containsKey("WakeLock4"));
-
-        KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1");
-        assertEquals(34, entry1.mCount);
-        assertEquals(123 * 1000, entry1.mTotalTime); // Microseconds
-
-        KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2");
-        assertEquals(46, entry2.mCount);
-        assertEquals(345 * 1000, entry2.mTotalTime); // Microseconds
-
-        KernelWakelockStats.Entry entry3 = staleStats.get("WakeLock3");
-        assertEquals(10, entry3.mCount);
-        assertEquals(1000, entry3.mTotalTime);
-
-        KernelWakelockStats.Entry entry4 = staleStats.get("WakeLock4");
-        assertEquals(20, entry4.mCount);
-        assertEquals(2000, entry4.mTotalTime);
-
-        buffer = new ProcFileBuilder()
-                .addLine("WakeLock1", 45, 789)  // Milliseconds
-                .addLine("WakeLock1", 56, 123)  // Milliseconds
-                .getBytes();
-        wlStats = new WakeLockInfo[1];
-        wlStats[0] = createWakeLockInfo("WakeLock4", 40, 4000);
-
-        readKernelWakelockStats(staleStats, buffer, wlStats);
-
-        assertEquals(2, staleStats.size());
-
-        assertTrue(staleStats.containsKey("WakeLock1"));
-        assertTrue(staleStats.containsKey("WakeLock4"));
-
-        assertFalse(staleStats.containsKey("WakeLock2"));
-        assertFalse(staleStats.containsKey("WakeLock3"));
-
-        entry1 = staleStats.get("WakeLock1");
-        assertEquals(45 + 56, entry1.mCount);
-        assertEquals((789 + 123) * 1000, entry1.mTotalTime);  // Microseconds
-
-        entry2 = staleStats.get("WakeLock4");
-        assertEquals(40, entry2.mCount);
-        assertEquals(4000, entry4.mTotalTime);
+        assertEquals(2000 * 1000, entry.mTotalTime); // Micro seconds
     }
 }
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index f01b1bf..54ac0bd 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -454,7 +454,7 @@
 // Canvas draw operations: Geometry
 // ----------------------------------------------------------------------------
 
-void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint,
+void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint,
                             SkCanvas::PointMode mode) {
     if (CC_UNLIKELY(count < 2 || paint.nothingToDraw())) return;
     // convert the floats into SkPoints
@@ -464,109 +464,142 @@
         pts[i].set(points[0], points[1]);
         points += 2;
     }
-    mCanvas->drawPoints(mode, count, pts.get(), *filterPaint(paint));
+
+    apply_looper(&paint, [&](const SkPaint& p) {
+        mCanvas->drawPoints(mode, count, pts.get(), p);
+    });
 }
 
-void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) {
-    mCanvas->drawPoint(x, y, *filterPaint(paint));
+void SkiaCanvas::drawPoint(float x, float y, const Paint& paint) {
+    apply_looper(&paint, [&](const SkPaint& p) {
+        mCanvas->drawPoint(x, y, p);
+    });
 }
 
-void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
-    this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kPoints_PointMode);
+void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint) {
+    this->drawPoints(points, count, paint, SkCanvas::kPoints_PointMode);
 }
 
 void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY,
-                          const SkPaint& paint) {
-    mCanvas->drawLine(startX, startY, stopX, stopY, *filterPaint(paint));
+                          const Paint& paint) {
+    apply_looper(&paint, [&](const SkPaint& p) {
+        mCanvas->drawLine(startX, startY, stopX, stopY, p);
+    });
 }
 
-void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
+void SkiaCanvas::drawLines(const float* points, int count, const Paint& paint) {
     if (CC_UNLIKELY(count < 4 || paint.nothingToDraw())) return;
-    this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kLines_PointMode);
+    this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode);
 }
 
-void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
+void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const Paint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
-    mCanvas->drawRect({left, top, right, bottom}, *filterPaint(paint));
+    apply_looper(&paint, [&](const SkPaint& p) {
+        mCanvas->drawRect({left, top, right, bottom}, p);
+    });
 }
 
-void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
+void SkiaCanvas::drawRegion(const SkRegion& region, const Paint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
-    mCanvas->drawRegion(region, *filterPaint(paint));
+    apply_looper(&paint, [&](const SkPaint& p) {
+        mCanvas->drawRegion(region, p);
+    });
 }
 
 void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
-                               const SkPaint& paint) {
+                               const Paint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
-    mCanvas->drawRoundRect(rect, rx, ry, *filterPaint(paint));
+    apply_looper(&paint, [&](const SkPaint& p) {
+        mCanvas->drawRoundRect(rect, rx, ry, p);
+    });
 }
 
 void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
-                                const SkPaint& paint) {
-    mCanvas->drawDRRect(outer, inner, *filterPaint(paint));
+                                const Paint& paint) {
+    apply_looper(&paint, [&](const SkPaint& p) {
+        mCanvas->drawDRRect(outer, inner, p);
+    });
 }
 
-void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
+void SkiaCanvas::drawCircle(float x, float y, float radius, const Paint& paint) {
     if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
-    mCanvas->drawCircle(x, y, radius, *filterPaint(paint));
+    apply_looper(&paint, [&](const SkPaint& p) {
+        mCanvas->drawCircle(x, y, radius, p);
+    });
 }
 
-void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
+void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const Paint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
-    mCanvas->drawOval(oval, *filterPaint(paint));
+    apply_looper(&paint, [&](const SkPaint& p) {
+        mCanvas->drawOval(oval, p);
+    });
 }
 
 void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle,
-                         float sweepAngle, bool useCenter, const SkPaint& paint) {
+                         float sweepAngle, bool useCenter, const Paint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect arc = SkRect::MakeLTRB(left, top, right, bottom);
-    if (fabs(sweepAngle) >= 360.0f) {
-        mCanvas->drawOval(arc, *filterPaint(paint));
-    } else {
-        mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, *filterPaint(paint));
-    }
+    apply_looper(&paint, [&](const SkPaint& p) {
+        if (fabs(sweepAngle) >= 360.0f) {
+            mCanvas->drawOval(arc, p);
+        } else {
+            mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, p);
+        }
+    });
 }
 
-void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+void SkiaCanvas::drawPath(const SkPath& path, const Paint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) {
         return;
     }
-    mCanvas->drawPath(path, *filterPaint(paint));
+    apply_looper(&paint, [&](const SkPaint& p) {
+        mCanvas->drawPath(path, p);
+    });
 }
 
-void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
-    mCanvas->drawVertices(vertices, mode, *filterPaint(paint));
+void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const Paint& paint) {
+    apply_looper(&paint, [&](const SkPaint& p) {
+        mCanvas->drawVertices(vertices, mode, p);
+    });
 }
 
 // ----------------------------------------------------------------------------
 // Canvas draw operations: Bitmaps
 // ----------------------------------------------------------------------------
 
-void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
-    mCanvas->drawImage(bitmap.makeImage(), left, top, filterPaint(paint));
+void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
+    auto image = bitmap.makeImage();
+    apply_looper(paint, [&](const SkPaint& p) {
+        mCanvas->drawImage(image, left, top, &p);
+    });
 }
 
-void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
+void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) {
+    auto image = bitmap.makeImage();
     SkAutoCanvasRestore acr(mCanvas, true);
     mCanvas->concat(matrix);
-    mCanvas->drawImage(bitmap.makeImage(), 0, 0, filterPaint(paint));
+    apply_looper(paint, [&](const SkPaint& p) {
+        mCanvas->drawImage(image, 0, 0, &p);
+    });
 }
 
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
                             float srcBottom, float dstLeft, float dstTop, float dstRight,
-                            float dstBottom, const SkPaint* paint) {
+                            float dstBottom, const Paint* paint) {
+    auto image = bitmap.makeImage();
     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    mCanvas->drawImageRect(bitmap.makeImage(), srcRect, dstRect, filterPaint(paint),
-                           SkCanvas::kFast_SrcRectConstraint);
+    apply_looper(paint, [&](const SkPaint& p) {
+        mCanvas->drawImageRect(image, srcRect, dstRect, &p, SkCanvas::kFast_SrcRectConstraint);
+    });
 }
 
 void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
-                                const float* vertices, const int* colors, const SkPaint* paint) {
+                                const float* vertices, const int* colors, const Paint* paint) {
     const int ptCount = (meshWidth + 1) * (meshHeight + 1);
     const int indexCount = meshWidth * meshHeight * 6;
     uint32_t flags = SkVertices::kHasTexCoords_BuilderFlag;
@@ -640,20 +673,20 @@
 #endif
 
     // cons-up a shader for the bitmap
-    PaintCoW paintCoW(paint);
-    SkPaint& tmpPaint = paintCoW.writeable();
-
-    sk_sp<SkImage> image = bitmap.makeImage();
-    sk_sp<SkShader> shader = image->makeShader();
-    tmpPaint.setShader(std::move(shader));
-
-    mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate,
-                          *filterPaint(std::move(paintCoW)));
+    Paint pnt;
+    if (paint) {
+        pnt = *paint;
+    }
+    pnt.setShader(bitmap.makeImage()->makeShader());
+    auto v = builder.detach();
+    apply_looper(&pnt, [&](const SkPaint& p) {
+        mCanvas->drawVertices(v, SkBlendMode::kModulate, p);
+    });
 }
 
 void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft,
                                float dstTop, float dstRight, float dstBottom,
-                               const SkPaint* paint) {
+                               const Paint* paint) {
     SkCanvas::Lattice lattice;
     NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height());
 
@@ -674,8 +707,10 @@
 
     lattice.fBounds = nullptr;
     SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
-
-    mCanvas->drawImageLattice(bitmap.makeImage().get(), lattice, dst, filterPaint(paint));
+    auto image = bitmap.makeImage();
+    apply_looper(paint, [&](const SkPaint& p) {
+        mCanvas->drawImageLattice(image.get(), lattice, dst, &p);
+    });
 }
 
 double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) {
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 799a891..ec83a19 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -22,8 +22,10 @@
 #include "RenderNode.h"
 #include "VectorDrawable.h"
 #include "hwui/Canvas.h"
+#include "hwui/Paint.h"
 
 #include <SkCanvas.h>
+#include "src/core/SkArenaAlloc.h"
 
 #include <cassert>
 #include <optional>
@@ -99,39 +101,39 @@
     virtual void drawColor(int color, SkBlendMode mode) override;
     virtual void drawPaint(const SkPaint& paint) override;
 
-    virtual void drawPoint(float x, float y, const SkPaint& paint) override;
-    virtual void drawPoints(const float* points, int count, const SkPaint& paint) override;
+    virtual void drawPoint(float x, float y, const Paint& paint) override;
+    virtual void drawPoints(const float* points, int count, const Paint& paint) override;
     virtual void drawLine(float startX, float startY, float stopX, float stopY,
-                          const SkPaint& paint) override;
-    virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
+                          const Paint& paint) override;
+    virtual void drawLines(const float* points, int count, const Paint& paint) override;
     virtual void drawRect(float left, float top, float right, float bottom,
-                          const SkPaint& paint) override;
-    virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
+                          const Paint& paint) override;
+    virtual void drawRegion(const SkRegion& region, const Paint& paint) override;
     virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
-                               const SkPaint& paint) override;
+                               const Paint& paint) override;
 
    virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
-                               const SkPaint& paint) override;
+                               const Paint& paint) override;
 
-    virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
+    virtual void drawCircle(float x, float y, float radius, const Paint& paint) override;
     virtual void drawOval(float left, float top, float right, float bottom,
-                          const SkPaint& paint) override;
+                          const Paint& paint) override;
     virtual void drawArc(float left, float top, float right, float bottom, float startAngle,
-                         float sweepAngle, bool useCenter, const SkPaint& paint) override;
-    virtual void drawPath(const SkPath& path, const SkPaint& paint) override;
-    virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) override;
+                         float sweepAngle, bool useCenter, const Paint& paint) override;
+    virtual void drawPath(const SkPath& path, const Paint& paint) override;
+    virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) override;
 
-    virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override;
-    virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override;
+    virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override;
+    virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override;
     virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
                             float srcBottom, float dstLeft, float dstTop, float dstRight,
-                            float dstBottom, const SkPaint* paint) override;
+                            float dstBottom, const Paint* paint) override;
     virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
                                 const float* vertices, const int* colors,
-                                const SkPaint* paint) override;
+                                const Paint* paint) override;
     virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft,
                                float dstTop, float dstRight, float dstBottom,
-                               const SkPaint* paint) override;
+                               const Paint* paint) override;
     virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) override;
 
     virtual bool drawTextAbsolutePos() const override { return true; }
@@ -208,6 +210,35 @@
      */
     PaintCoW&& filterPaint(PaintCoW&& paint) const;
 
+    template <typename Proc> void apply_looper(const Paint* paint, Proc proc) {
+        SkPaint skp;
+        SkDrawLooper* looper = nullptr;
+        if (paint) {
+            skp = *filterPaint(paint);
+            looper = paint->getLooper();
+        }
+        if (looper) {
+            SkSTArenaAlloc<256> alloc;
+            SkDrawLooper::Context* ctx = looper->makeContext(&alloc);
+            if (ctx) {
+                SkDrawLooper::Context::Info info;
+                for (;;) {
+                    SkPaint p = skp;
+                    if (!ctx->next(&info, &p)) {
+                        break;
+                    }
+                    mCanvas->save();
+                    mCanvas->translate(info.fTranslate.fX, info.fTranslate.fY);
+                    proc(p);
+                    mCanvas->restore();
+                }
+            }
+        } else {
+            proc(skp);
+        }
+    }
+
+
 private:
     struct SaveRec {
         int saveCount;
@@ -222,7 +253,7 @@
     void recordClip(const T&, SkClipOp);
     void applyPersistentClips(size_t clipStartIndex);
 
-    void drawPoints(const float* points, int count, const SkPaint& paint, SkCanvas::PointMode mode);
+    void drawPoints(const float* points, int count, const Paint& paint, SkCanvas::PointMode mode);
 
     class Clip;
 
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 89ad1b9..f91d178 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -17,6 +17,7 @@
 #include "VectorDrawable.h"
 
 #include <utils/Log.h>
+#include "hwui/Paint.h"
 #include "PathParser.h"
 #include "SkColorFilter.h"
 #include "SkImageInfo.h"
@@ -458,8 +459,12 @@
         mStagingCache.dirty = false;
     }
 
-    SkPaint paint;
-    getPaintFor(&paint, mStagingProperties);
+    SkPaint skp;
+    getPaintFor(&skp, mStagingProperties);
+    Paint paint;
+    paint.setFilterQuality(skp.getFilterQuality());
+    paint.setColorFilter(skp.refColorFilter());
+    paint.setAlpha(skp.getAlpha());
     outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0, mStagingCache.bitmap->width(),
                           mStagingCache.bitmap->height(), mStagingProperties.getBounds().left(),
                           mStagingProperties.getBounds().top(),
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index a48c860..b98ffca 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -34,7 +34,7 @@
 }
 
 static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
-                              const SkPaint& paint, Canvas* canvas) {
+                              const Paint& paint, Canvas* canvas) {
     const SkScalar strokeWidth = fmax(thickness, 1.0f);
     const SkScalar bottom = top + strokeWidth;
     canvas->drawRect(left, top, right, bottom, paint);
@@ -182,7 +182,7 @@
 void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight,
                             float outerBottom, float outerRx, float outerRy, float innerLeft,
                             float innerTop, float innerRight, float innerBottom, float innerRx,
-                            float innerRy, const SkPaint& paint) {
+                            float innerRy, const Paint& paint) {
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom);
     SkRect inner = SkRect::MakeLTRB(innerLeft, innerTop, innerRight, innerBottom);
@@ -198,7 +198,7 @@
 void Canvas::drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight,
                             float outerBottom, const float* outerRadii, float innerLeft,
                             float innerTop, float innerRight, float innerBottom,
-                            const float* innerRadii, const SkPaint& paint) {
+                            const float* innerRadii, const Paint& paint) {
     static_assert(sizeof(SkVector) == sizeof(float) * 2);
     if (CC_UNLIKELY(paint.nothingToDraw())) return;
     SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom);
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index ee4fa1d6..b90a4a3 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -217,37 +217,37 @@
     virtual void drawPaint(const SkPaint& paint) = 0;
 
     // Geometry
-    virtual void drawPoint(float x, float y, const SkPaint& paint) = 0;
-    virtual void drawPoints(const float* points, int floatCount, const SkPaint& paint) = 0;
+    virtual void drawPoint(float x, float y, const Paint& paint) = 0;
+    virtual void drawPoints(const float* points, int floatCount, const Paint& paint) = 0;
     virtual void drawLine(float startX, float startY, float stopX, float stopY,
-                          const SkPaint& paint) = 0;
-    virtual void drawLines(const float* points, int floatCount, const SkPaint& paint) = 0;
+                          const Paint& paint) = 0;
+    virtual void drawLines(const float* points, int floatCount, const Paint& paint) = 0;
     virtual void drawRect(float left, float top, float right, float bottom,
-                          const SkPaint& paint) = 0;
-    virtual void drawRegion(const SkRegion& region, const SkPaint& paint) = 0;
+                          const Paint& paint) = 0;
+    virtual void drawRegion(const SkRegion& region, const Paint& paint) = 0;
     virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
-                               const SkPaint& paint) = 0;
+                               const Paint& paint) = 0;
     virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
-                                const SkPaint& paint) = 0;
-    virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0;
+                                const Paint& paint) = 0;
+    virtual void drawCircle(float x, float y, float radius, const Paint& paint) = 0;
     virtual void drawOval(float left, float top, float right, float bottom,
-                          const SkPaint& paint) = 0;
+                          const Paint& paint) = 0;
     virtual void drawArc(float left, float top, float right, float bottom, float startAngle,
-                         float sweepAngle, bool useCenter, const SkPaint& paint) = 0;
-    virtual void drawPath(const SkPath& path, const SkPaint& paint) = 0;
-    virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) = 0;
+                         float sweepAngle, bool useCenter, const Paint& paint) = 0;
+    virtual void drawPath(const SkPath& path, const Paint& paint) = 0;
+    virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) = 0;
 
     // Bitmap-based
-    virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) = 0;
-    virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) = 0;
+    virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) = 0;
+    virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) = 0;
     virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
                             float srcBottom, float dstLeft, float dstTop, float dstRight,
-                            float dstBottom, const SkPaint* paint) = 0;
+                            float dstBottom, const Paint* paint) = 0;
     virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
-                                const float* vertices, const int* colors, const SkPaint* paint) = 0;
+                                const float* vertices, const int* colors, const Paint* paint) = 0;
     virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft,
                                float dstTop, float dstRight, float dstBottom,
-                               const SkPaint* paint) = 0;
+                               const Paint* paint) = 0;
 
     virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) = 0;
     virtual void drawPicture(const SkPicture& picture) = 0;
@@ -281,12 +281,12 @@
     void drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight,
                                 float outerBottom, float outerRx, float outerRy, float innerLeft,
                                 float innerTop, float innerRight, float innerBottom, float innerRx,
-                                float innerRy, const SkPaint& paint);
+                                float innerRy, const Paint& paint);
 
     void drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight,
                                 float outerBottom, const float* outerRadii, float innerLeft,
                                 float innerTop, float innerRight, float innerBottom,
-                                const float* innerRadii, const SkPaint& paint);
+                                const float* innerRadii, const Paint& paint);
 
     static int GetApiLevel() { return sApiLevel; }
 
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 9b2fa9d..281ecd2 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -21,6 +21,7 @@
 
 #include <cutils/compiler.h>
 
+#include <SkDrawLooper.h>
 #include <SkFont.h>
 #include <SkPaint.h>
 #include <string>
@@ -58,12 +59,17 @@
     SkFont& getSkFont() { return mFont; }
     const SkFont& getSkFont() const { return mFont; }
 
+    SkDrawLooper* getLooper() const { return mLooper.get(); }
+    void setLooper(sk_sp<SkDrawLooper> looper) { mLooper = std::move(looper); }
+
     // These shadow the methods on SkPaint, but we need to so we can keep related
     // attributes in-sync.
 
     void reset();
     void setAntiAlias(bool);
 
+    bool nothingToDraw() const { return !mLooper && SkPaint::nothingToDraw(); }
+
     // End method shadowing
 
     void setLetterSpacing(float letterSpacing) { mLetterSpacing = letterSpacing; }
@@ -146,6 +152,7 @@
  
 private:
     SkFont mFont;
+    sk_sp<SkDrawLooper> mLooper;
 
     float mLetterSpacing = 0;
     float mWordSpacing = 0;
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index 2f2d575..fa2674f 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -34,6 +34,7 @@
 Paint::Paint(const Paint& paint)
         : SkPaint(paint)
         , mFont(paint.mFont)
+        , mLooper(paint.mLooper)
         , mLetterSpacing(paint.mLetterSpacing)
         , mWordSpacing(paint.mWordSpacing)
         , mFontFeatureSettings(paint.mFontFeatureSettings)
@@ -52,6 +53,7 @@
 Paint& Paint::operator=(const Paint& other) {
     SkPaint::operator=(other);
     mFont = other.mFont;
+    mLooper = other.mLooper;
     mLetterSpacing = other.mLetterSpacing;
     mWordSpacing = other.mWordSpacing;
     mFontFeatureSettings = other.mFontFeatureSettings;
@@ -69,6 +71,7 @@
 bool operator==(const Paint& a, const Paint& b) {
     return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) &&
            a.mFont == b.mFont &&
+           a.mLooper == b.mLooper && 
            a.mLetterSpacing == b.mLetterSpacing && a.mWordSpacing == b.mWordSpacing &&
            a.mFontFeatureSettings == b.mFontFeatureSettings &&
            a.mMinikinLocaleListId == b.mMinikinLocaleListId &&
@@ -83,6 +86,7 @@
 
     mFont = SkFont();
     mFont.setEdging(SkFont::Edging::kAlias);
+    mLooper.reset();
 
     mStrikeThru = false;
     mUnderline = false;
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 96b17e1..b017384 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -33,6 +33,10 @@
     }
 }
 
+static inline SkScalar isIntegerAligned(SkScalar x) {
+    return fabsf(roundf(x) - x) <= NON_ZERO_EPSILON;
+}
+
 // Disable filtering when there is no scaling in screen coordinates and the corners have the same
 // fraction (for translate) or zero fraction (for any other rect-to-rect transform).
 static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, const SkRect& dstRect) {
@@ -62,10 +66,10 @@
     if (requiresIntegerTranslate) {
         // Device rect and source rect should be integer aligned to ensure there's no difference
         // in how nearest-neighbor sampling is resolved.
-        return !(MathUtils::isZero(SkScalarFraction(srcRect.x())) &&
-                 MathUtils::isZero(SkScalarFraction(srcRect.y())) &&
-                 MathUtils::isZero(SkScalarFraction(dstDevRect.x())) &&
-                 MathUtils::isZero(SkScalarFraction(dstDevRect.y())));
+        return !(isIntegerAligned(srcRect.x()) &&
+                 isIntegerAligned(srcRect.y()) &&
+                 isIntegerAligned(dstDevRect.x()) &&
+                 isIntegerAligned(dstDevRect.y()));
     } else {
         // As long as src and device rects are translated by the same fractional amount,
         // filtering won't be needed
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 38ef131..0db5133 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -15,7 +15,7 @@
  */
 
 #include "SkiaRecordingCanvas.h"
-
+#include "hwui/Paint.h"
 #include <SkImagePriv.h>
 #include "CanvasTransform.h"
 #ifdef __ANDROID__ // Layoutlib does not support Layers
@@ -197,9 +197,37 @@
     return filterPaint(std::move(paint));
 }
 
-void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
+static SkDrawLooper* get_looper(const Paint* paint) {
+    return paint ? paint->getLooper() : nullptr;
+}
+
+template <typename Proc>
+void applyLooper(SkDrawLooper* looper, const SkPaint& paint, Proc proc) {
+    if (looper) {
+        SkSTArenaAlloc<256> alloc;
+        SkDrawLooper::Context* ctx = looper->makeContext(&alloc);
+        if (ctx) {
+            SkDrawLooper::Context::Info info;
+            for (;;) {
+                SkPaint p = paint;
+                if (!ctx->next(&info, &p)) {
+                    break;
+                }
+                proc(info.fTranslate.fX, info.fTranslate.fY, p);
+            }
+        }
+    } else {
+        proc(0, 0, paint);
+    }
+}
+
+void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
     sk_sp<SkImage> image = bitmap.makeImage();
-    mRecorder.drawImage(image, left, top, filterBitmap(paint), bitmap.palette());
+
+    applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) {
+        mRecorder.drawImage(image, left + x, top + y, &p, bitmap.palette());
+    });
+
     // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
     // it is not safe to store a raw SkImage pointer, because the image object will be destroyed
     // when this function ends.
@@ -208,12 +236,16 @@
     }
 }
 
-void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
+void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) {
     SkAutoCanvasRestore acr(&mRecorder, true);
     concat(matrix);
 
     sk_sp<SkImage> image = bitmap.makeImage();
-    mRecorder.drawImage(image, 0, 0, filterBitmap(paint), bitmap.palette());
+
+    applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) {
+        mRecorder.drawImage(image, x, y, &p, bitmap.palette());
+    });
+
     if (!bitmap.isImmutable() && image.get() && !image->unique()) {
         mDisplayList->mMutableImages.push_back(image.get());
     }
@@ -221,13 +253,17 @@
 
 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
                                      float srcBottom, float dstLeft, float dstTop, float dstRight,
-                                     float dstBottom, const SkPaint* paint) {
+                                     float dstBottom, const Paint* paint) {
     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
     sk_sp<SkImage> image = bitmap.makeImage();
-    mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint),
-                            SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
+
+    applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) {
+        mRecorder.drawImageRect(image, srcRect, dstRect.makeOffset(x, y), &p,
+                                SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
+    });
+
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
         !dstRect.isEmpty()) {
         mDisplayList->mMutableImages.push_back(image.get());
@@ -236,7 +272,7 @@
 
 void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft,
                                         float dstTop, float dstRight, float dstBottom,
-                                        const SkPaint* paint) {
+                                        const Paint* paint) {
     SkCanvas::Lattice lattice;
     NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height());
 
@@ -264,8 +300,11 @@
         filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
     }
     sk_sp<SkImage> image = bitmap.makeImage();
-    mRecorder.drawImageLattice(image, lattice, dst, filterBitmap(std::move(filteredPaint)),
-                               bitmap.palette());
+
+    applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) {
+        mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), &p, bitmap.palette());
+    });
+
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
         mDisplayList->mMutableImages.push_back(image.get());
     }
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index c42cea3..bd5274c 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -45,14 +45,14 @@
 
     virtual uirenderer::DisplayList* finishRecording() override;
 
-    virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override;
-    virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override;
+    virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override;
+    virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override;
     virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
                             float srcBottom, float dstLeft, float dstTop, float dstRight,
-                            float dstBottom, const SkPaint* paint) override;
+                            float dstBottom, const Paint* paint) override;
     virtual void drawNinePatch(Bitmap& hwuiBitmap, const android::Res_png_9patch& chunk,
                                float dstLeft, float dstTop, float dstRight, float dstBottom,
-                               const SkPaint* paint) override;
+                               const Paint* paint) override;
     virtual double drawAnimatedImage(AnimatedImageDrawable* animatedImage) override;
 
     virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d97c5ed..d19351b 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -450,20 +450,38 @@
     waitOnFences();
 
     bool requireSwap = false;
+    int error = OK;
     bool didSwap =
             mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap);
 
     mIsDirty = false;
 
     if (requireSwap) {
-        if (!didSwap) {  // some error happened
+        bool didDraw = true;
+        // Handle any swapchain errors
+        error = mNativeSurface->getAndClearError();
+        if (error == TIMED_OUT) {
+            // Try again
+            mRenderThread.postFrameCallback(this);
+            // But since this frame didn't happen, we need to mark full damage in the swap
+            // history
+            didDraw = false;
+
+        } else if (error != OK || !didSwap) {
+            // Unknown error, abandon the surface
             setSurface(nullptr);
+            didDraw = false;
         }
+
         SwapHistory& swap = mSwapHistory.next();
-        swap.damage = windowDirty;
+        if (didDraw) {
+            swap.damage = windowDirty;
+        } else {
+            swap.damage = SkRect::MakeWH(INT_MAX, INT_MAX);
+        }
         swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC);
         swap.vsyncTime = mRenderThread.timeLord().latestVsync();
-        if (mNativeSurface.get()) {
+        if (didDraw) {
             int durationUs;
             nsecs_t dequeueStart = mNativeSurface->getLastDequeueStartTime();
             if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) {
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp
index ad1fc49..a44b804 100644
--- a/libs/hwui/renderthread/ReliableSurface.cpp
+++ b/libs/hwui/renderthread/ReliableSurface.cpp
@@ -87,21 +87,21 @@
 }
 
 int ReliableSurface::reserveNext() {
+    if constexpr (DISABLE_BUFFER_PREFETCH) {
+        return OK;
+    }
     {
         std::lock_guard _lock{mMutex};
         if (mReservedBuffer) {
             ALOGW("reserveNext called but there was already a buffer reserved?");
             return OK;
         }
-        if (mInErrorState) {
+        if (mBufferQueueState != OK) {
             return UNKNOWN_ERROR;
         }
         if (mHasDequeuedBuffer) {
             return OK;
         }
-        if constexpr (DISABLE_BUFFER_PREFETCH) {
-            return OK;
-        }
     }
 
     // TODO: Update this to better handle when requested dimensions have changed
@@ -165,10 +165,11 @@
         }
     }
 
+
     int result = callProtected(mSurface, dequeueBuffer, buffer, fenceFd);
     if (result != OK) {
         ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result);
-        *buffer = acquireFallbackBuffer();
+        *buffer = acquireFallbackBuffer(result);
         *fenceFd = -1;
         return *buffer ? OK : INVALID_OPERATION;
     } else {
@@ -201,9 +202,9 @@
     return windowBuffer == scratchBuffer;
 }
 
-ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer() {
+ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer(int error) {
     std::lock_guard _lock{mMutex};
-    mInErrorState = true;
+    mBufferQueueState = error;
 
     if (mScratchBuffer) {
         return AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get());
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index 0bfc72e..41fc35e 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -43,6 +43,12 @@
 
     uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); }
 
+    int getAndClearError() {
+        int ret = mBufferQueueState;
+        mBufferQueueState = OK;
+        return ret;
+    }
+
 private:
     const sp<Surface> mSurface;
 
@@ -55,10 +61,10 @@
     ANativeWindowBuffer* mReservedBuffer = nullptr;
     base::unique_fd mReservedFenceFd;
     bool mHasDequeuedBuffer = false;
-    bool mInErrorState = false;
+    int mBufferQueueState = OK;
 
     bool isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const;
-    ANativeWindowBuffer* acquireFallbackBuffer();
+    ANativeWindowBuffer* acquireFallbackBuffer(int error);
     void clearReservedBuffer();
 
     void perform(int operation, va_list args);
diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
index 400196b..c4067af 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <SkImagePriv.h>
+#include "hwui/Paint.h"
 #include "TestSceneBase.h"
 #include "tests/common/BitmapAllocationTestUtils.h"
 #include "utils/Color.h"
@@ -43,7 +44,7 @@
                     skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint);
                 });
 
-        SkPaint paint;
+        Paint paint;
         sk_sp<SkImage> image = hwuiBitmap->makeImage();
         sk_sp<SkShader> repeatShader =
                 image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat);
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index 659926b..3d0a2b2 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -65,7 +65,7 @@
         sk_sp<SkShader> compositeShader(
                 SkShaders::Blend(SkBlendMode::kDstATop, hardwareShader, gradientShader));
 
-        SkPaint paint;
+        Paint paint;
         paint.setShader(std::move(compositeShader));
         canvas.drawRoundRect(0, 0, 400, 200, 10.0f, 10.0f, paint);
     }
diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
index c6b60aae..a9449b6 100644
--- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
@@ -49,7 +49,7 @@
         SkMatrix matrix;
         matrix.setScale(1, length);
         matrix.postRotate(-90);
-        SkPaint fadingPaint;
+        Paint fadingPaint;
         fadingPaint.setShader(s->makeWithLocalMatrix(matrix));
         fadingPaint.setBlendMode(SkBlendMode::kDstOut);
         canvas.drawRect(0, 0, length, itemHeight, fadingPaint);
diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
index fb7e34d..d031923 100644
--- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
@@ -79,7 +79,7 @@
         static sk_sp<Bitmap> filledBox(createBoxBitmap(true));
         static sk_sp<Bitmap> strokedBox(createBoxBitmap(false));
         // TODO: switch to using round rect clipping, once merging correctly handles that
-        SkPaint roundRectPaint;
+        Paint roundRectPaint;
         roundRectPaint.setAntiAlias(true);
         roundRectPaint.setColor(Color::White);
         canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint);
diff --git a/libs/hwui/tests/common/scenes/OvalAnimation.cpp b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
index 4ff868b..402c1ec 100644
--- a/libs/hwui/tests/common/scenes/OvalAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
@@ -28,7 +28,7 @@
     void createContent(int width, int height, Canvas& canvas) override {
         canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
         card = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, Canvas& canvas) {
-            SkPaint paint;
+            Paint paint;
             paint.setAntiAlias(true);
             paint.setColor(Color::Black);
             canvas.drawOval(0, 0, 200, 200, paint);
diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
index 6a3b6a5..d5ecfaf 100644
--- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
@@ -41,7 +41,7 @@
                 }
             }
 
-            SkPaint paint;
+            Paint paint;
             paint.setColor(0xff00ffff);
             canvas.drawRegion(region, paint);
         });
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 02dd42f..97bfba3 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -45,7 +45,7 @@
             canvas.save(SaveFlags::MatrixClip);
             canvas.translate(0, 400);
             canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::Flags(0));  // unclipped
-            SkPaint paint;
+            Paint paint;
             paint.setAntiAlias(true);
             paint.setColor(Color::Green_700);
             canvas.drawCircle(200, 200, 200, paint);
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index d189a93..70a1557 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -30,14 +30,14 @@
     void createContent(int width, int height, Canvas& canvas) override {
         card = TestUtils::createNode(
                 0, 0, width, height, [width](RenderProperties& props, Canvas& canvas) {
-                    std::function<void(Canvas&, float, const SkPaint&)> ops[] = {
-                            [](Canvas& canvas, float size, const SkPaint& paint) {
+                    std::function<void(Canvas&, float, const Paint&)> ops[] = {
+                            [](Canvas& canvas, float size, const Paint& paint) {
                                 canvas.drawArc(0, 0, size, size, 50, 189, true, paint);
                             },
-                            [](Canvas& canvas, float size, const SkPaint& paint) {
+                            [](Canvas& canvas, float size, const Paint& paint) {
                                 canvas.drawOval(0, 0, size, size, paint);
                             },
-                            [](Canvas& canvas, float size, const SkPaint& paint) {
+                            [](Canvas& canvas, float size, const Paint& paint) {
                                 SkPath diamondPath;
                                 diamondPath.moveTo(size / 2, 0);
                                 diamondPath.lineTo(size, size / 2);
@@ -46,18 +46,18 @@
                                 diamondPath.close();
                                 canvas.drawPath(diamondPath, paint);
                             },
-                            [](Canvas& canvas, float size, const SkPaint& paint) {
+                            [](Canvas& canvas, float size, const Paint& paint) {
                                 float data[] = {0, 0, size, size, 0, size, size, 0};
                                 canvas.drawLines(data, sizeof(data) / sizeof(float), paint);
                             },
-                            [](Canvas& canvas, float size, const SkPaint& paint) {
+                            [](Canvas& canvas, float size, const Paint& paint) {
                                 float data[] = {0, 0, size, size, 0, size, size, 0};
                                 canvas.drawPoints(data, sizeof(data) / sizeof(float), paint);
                             },
-                            [](Canvas& canvas, float size, const SkPaint& paint) {
+                            [](Canvas& canvas, float size, const Paint& paint) {
                                 canvas.drawRect(0, 0, size, size, paint);
                             },
-                            [](Canvas& canvas, float size, const SkPaint& paint) {
+                            [](Canvas& canvas, float size, const Paint& paint) {
                                 float rad = size / 4;
                                 canvas.drawRoundRect(0, 0, size, size, rad, rad, paint);
                             }};
@@ -66,7 +66,7 @@
 
                     // each combination of strokeWidth + style gets a column
                     int outerCount = canvas.save(SaveFlags::MatrixClip);
-                    SkPaint paint;
+                    Paint paint;
                     paint.setAntiAlias(true);
                     SkPaint::Style styles[] = {SkPaint::kStroke_Style, SkPaint::kFill_Style,
                                                SkPaint::kStrokeAndFill_Style};
diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
index e60bd5f..a0bc5aa 100644
--- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
@@ -52,7 +52,7 @@
         return TestUtils::createNode(
                 x, y, x + width, y + height,
                 [width, height](RenderProperties& props, Canvas& canvas) {
-                    SkPaint paint;
+                    Paint paint;
                     // Simple scale/translate case where R, G, and B are all treated equivalently
                     SkColorMatrix cm;
                     cm.setScale(1.1f, 1.1f, 1.1f, 0.5f);
diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
index 8bd804e..57a260c 100644
--- a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
@@ -51,7 +51,7 @@
                 [width, height](RenderProperties& props, Canvas& canvas) {
                     float pos[] = {0, 1};
                     SkPoint pts[] = {SkPoint::Make(0, 0), SkPoint::Make(width, height)};
-                    SkPaint paint;
+                    Paint paint;
                     // overdraw several times to emphasize shader cost
                     for (int i = 0; i < 10; i++) {
                         // use i%2 start position to pick 2 color combo with black in it
diff --git a/libs/hwui/tests/common/scenes/TestSceneBase.h b/libs/hwui/tests/common/scenes/TestSceneBase.h
index 6f76a50..24d3585 100644
--- a/libs/hwui/tests/common/scenes/TestSceneBase.h
+++ b/libs/hwui/tests/common/scenes/TestSceneBase.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include "hwui/Canvas.h"
+#include "hwui/Paint.h"
 #include "RenderNode.h"
 #include "tests/common/TestContext.h"
 #include "tests/common/TestScene.h"
diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp
index 76f0288..bac8870 100644
--- a/libs/hwui/tests/common/scenes/TvApp.cpp
+++ b/libs/hwui/tests/common/scenes/TvApp.cpp
@@ -217,7 +217,7 @@
             std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(
                     image->stagingProperties().getWidth(), image->stagingProperties().getHeight(),
                     image.get()));
-            SkPaint paint;
+            Paint paint;
             sk_sp<SkColorFilter> filter(
                     SkColorFilters::Blend((curFrame % 150) << 24, SkBlendMode::kSrcATop));
             paint.setColorFilter(filter);
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 70423a7..4ce6c32 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -18,6 +18,7 @@
 
 #include "DisplayList.h"
 #include "hwui/Canvas.h"
+#include "hwui/Paint.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "tests/common/TestUtils.h"
 
@@ -93,7 +94,7 @@
     std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
     delete canvas->finishRecording();
 
-    SkPaint rectPaint;
+    Paint rectPaint;
     sk_sp<Bitmap> iconBitmap(TestUtils::createBitmap(80, 80));
 
     while (benchState.KeepRunning()) {
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index e70378b..3632be0 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -24,6 +24,7 @@
 #include "DamageAccumulator.h"
 #include "FatalTestCanvas.h"
 #include "IContextFactory.h"
+#include "hwui/Paint.h"
 #include "RecordingCanvas.h"
 #include "SkiaCanvas.h"
 #include "pipeline/skia/SkiaDisplayList.h"
@@ -59,7 +60,7 @@
 namespace {
 
 static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) {
-    SkPaint paint;
+    Paint paint;
     // order put in blue channel, transparent so overlapped content doesn't get rejected
     paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder));
     canvas->drawRect(0, 0, 100, 100, paint);
@@ -211,7 +212,7 @@
                 ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
                 EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
 
-                SkPaint paint;
+                Paint paint;
                 paint.setAntiAlias(true);
                 paint.setColor(SK_ColorGREEN);
                 recorder.drawRect(0.0f, 400.0f, 400.0f, 800.0f, paint);
@@ -291,7 +292,7 @@
                 properties.setTranslationX(SCROLL_X);
                 properties.setTranslationY(SCROLL_Y);
 
-                SkPaint paint;
+                Paint paint;
                 paint.setColor(SK_ColorWHITE);
                 canvas.drawRect(0, 0, 100, 100, paint);
             },
@@ -302,7 +303,7 @@
             [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
                 properties.setProjectBackwards(true);
                 properties.setClipToBounds(false);
-                SkPaint paint;
+                Paint paint;
                 paint.setColor(SK_ColorDKGRAY);
                 canvas.drawRect(-10, -10, 60, 60, paint);
             },
@@ -310,7 +311,7 @@
     auto child = TestUtils::createSkiaNode(
             0, 50, 100, 100,
             [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
-                SkPaint paint;
+                Paint paint;
                 paint.setColor(SK_ColorBLUE);
                 canvas.drawRect(0, 0, 100, 50, paint);
                 canvas.drawRenderNode(projectingRipple.get());
@@ -375,14 +376,14 @@
             [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
                 properties.setProjectBackwards(true);
                 properties.setClipToBounds(false);
-                SkPaint paint;
+                Paint paint;
                 canvas.drawRect(0, 0, 100, 100, paint);
             },
             "P");
     auto child = TestUtils::createSkiaNode(
             0, 0, 100, 100,
             [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
-                SkPaint paint;
+                Paint paint;
                 canvas.drawRect(0, 0, 100, 100, paint);
                 canvas.drawRenderNode(projectingRipple.get());
             },
@@ -483,7 +484,7 @@
                 properties.setTranslationX(SCROLL_X);
                 properties.setTranslationY(SCROLL_Y);
 
-                canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
+                canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, Paint());
             },
             "B");  // B
     auto projectingRipple = TestUtils::createSkiaNode(
@@ -491,14 +492,14 @@
             [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
                 properties.setProjectBackwards(true);
                 properties.setClipToBounds(false);
-                canvas.drawOval(100, 100, 300, 300, SkPaint());  // drawn mostly out of layer bounds
+                canvas.drawOval(100, 100, 300, 300, Paint());  // drawn mostly out of layer bounds
             },
             "R");  // R
     auto child = TestUtils::createSkiaNode(
             100, 100, 300, 300,
             [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
                 canvas.drawRenderNode(projectingRipple.get());
-                canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint());
+                canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, Paint());
             },
             "C");  // C
     auto parent = TestUtils::createSkiaNode(
@@ -578,7 +579,7 @@
             0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
             [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
                 properties.setProjectionReceiver(true);
-                canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint());
+                canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, Paint());
             },
             "B");  // B
     auto projectingRipple = TestUtils::createSkiaNode(
@@ -591,7 +592,7 @@
                 properties.setTranslationY(SCROLL_Y);
                 properties.setProjectBackwards(true);
                 properties.setClipToBounds(false);
-                canvas.drawOval(0, 0, 200, 200, SkPaint());
+                canvas.drawOval(0, 0, 200, 200, Paint());
             },
             "R");  // R
     auto child = TestUtils::createSkiaNode(
@@ -946,7 +947,7 @@
                                           [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
                                               sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
                                               canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
-                                                              SkPaint());
+                                                              Paint());
                                               canvas.drawBitmap(*bitmap, 10, 10, nullptr);
                                           });
 
@@ -1022,7 +1023,7 @@
 
     auto child = TestUtils::createSkiaNode(
             10, 10, 110, 110, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
-                SkPaint paint;
+                Paint paint;
                 paint.setColor(SK_ColorWHITE);
                 canvas.drawRect(0, 0, 100, 100, paint);
             });
@@ -1030,7 +1031,7 @@
     auto parent = TestUtils::createSkiaNode(
             0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
             [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) {
-                SkPaint paint;
+                Paint paint;
                 paint.setColor(SK_ColorDKGRAY);
                 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
 
@@ -1065,7 +1066,7 @@
     auto layerNode = TestUtils::createSkiaNode(
             0, 0, LAYER_WIDTH, LAYER_HEIGHT,
             [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
-                canvas.drawPaint(SkPaint());
+                canvas.drawPaint(Paint());
             });
 
     layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 2ed1b25..fcc64fd 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -16,6 +16,7 @@
 
 #include "tests/common/TestUtils.h"
 
+#include <hwui/Paint.h>
 #include <SkBlurDrawLooper.h>
 #include <SkCanvasStateUtils.h>
 #include <SkPicture.h>
@@ -32,7 +33,7 @@
     // clear to white
     canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrc);
 
-    SkPaint paint;
+    Paint paint;
     // it is transparent to ensure that we still draw the rect since it has a looper
     paint.setColor(SK_ColorTRANSPARENT);
     // this is how view's shadow layers are implemented
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 7b76bd8..958baa7 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -23,6 +23,7 @@
 #include "AnimationContext.h"
 #include "DamageAccumulator.h"
 #include "IContextFactory.h"
+#include "hwui/Paint.h"
 #include "SkiaCanvas.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "pipeline/skia/SkiaOpenGLPipeline.h"
@@ -96,7 +97,7 @@
 RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
     auto halfGreenNode = TestUtils::createSkiaNode(
             0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
-                SkPaint greenPaint;
+                Paint greenPaint;
                 greenPaint.setColor(SK_ColorGREEN);
                 greenPaint.setStyle(SkPaint::kFill_Style);
                 bottomHalfGreenCanvas.drawRect(0, 1, 2, 2, greenPaint);
@@ -294,7 +295,7 @@
     };
 
     std::vector<sp<RenderNode>> nodes;
-    SkPaint transparentPaint;
+    Paint transparentPaint;
     transparentPaint.setAlpha(128);
 
     // backdrop
diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
index 635429d..eec25c6 100644
--- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
@@ -24,6 +24,7 @@
 #include "DamageAccumulator.h"
 #include "FatalTestCanvas.h"
 #include "IContextFactory.h"
+#include "hwui/Paint.h"
 #include "SkiaCanvas.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "pipeline/skia/SkiaPipeline.h"
@@ -60,7 +61,7 @@
             0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
             [propSetupCallback](RenderProperties& props, SkiaRecordingCanvas& canvas) {
                 propSetupCallback(props);
-                SkPaint paint;
+                Paint paint;
                 paint.setColor(SK_ColorWHITE);
                 canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint);
             });
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index ebf2343..e2fdf2f 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -67,29 +67,6 @@
         return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
     }
 
-    struct TextShadow {
-        SkScalar radius;
-        float dx;
-        float dy;
-        SkColor color;
-    };
-
-    static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) {
-        SkDrawLooper::BlurShadowRec blur;
-        if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) {
-            if (textShadow) {
-                textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma);
-                textShadow->dx = blur.fOffset.fX;
-                textShadow->dy = blur.fOffset.fY;
-                textShadow->color = blur.fColor;
-            }
-            return true;
-        }
-        return false;
-    }
-
-    static inline bool hasTextShadow(const SkPaint* paint) { return getTextShadow(paint, nullptr); }
-
     static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) {
         return paint ? paint->getBlendMode() : SkBlendMode::kSrcOver;
     }
diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp
index 1e9a194..357bb5e5 100644
--- a/media/native/midi/amidi.cpp
+++ b/media/native/midi/amidi.cpp
@@ -122,7 +122,7 @@
  *  - AMEDIA_ERROR_INVALID_PARAMETER
  *  AMEDIA_ERROR_UNKNOWN
  */
-static media_status_t AMIDI_API AMIDI_getDeviceInfo(const AMidiDevice *device,
+static media_status_t AMIDI_getDeviceInfo(const AMidiDevice *device,
         AMidiDeviceInfo *outDeviceInfoPtr) {
     if (device == nullptr) {
         return AMEDIA_ERROR_INVALID_PARAMETER;
@@ -142,7 +142,7 @@
     return AMEDIA_OK;
 }
 
-media_status_t AMIDI_API AMidiDevice_fromJava(JNIEnv *env, jobject j_midiDeviceObj,
+media_status_t AMidiDevice_fromJava(JNIEnv *env, jobject j_midiDeviceObj,
         AMidiDevice** devicePtrPtr)
 {
     if (j_midiDeviceObj == nullptr) {
@@ -188,7 +188,7 @@
     return AMEDIA_OK;
 }
 
-media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *device)
+media_status_t AMidiDevice_release(const AMidiDevice *device)
 {
     if (device == nullptr || device->midiDeviceObj == nullptr) {
         return AMEDIA_ERROR_INVALID_PARAMETER;
@@ -217,21 +217,21 @@
     return AMEDIA_OK;
 }
 
-int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) {
+int32_t AMidiDevice_getType(const AMidiDevice *device) {
     if (device == nullptr) {
         return AMEDIA_ERROR_INVALID_PARAMETER;
     }
     return device->deviceInfo.type;
 }
 
-ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) {
+ssize_t AMidiDevice_getNumInputPorts(const AMidiDevice *device) {
     if (device == nullptr) {
         return AMEDIA_ERROR_INVALID_PARAMETER;
     }
     return device->deviceInfo.inputPortCount;
 }
 
-ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) {
+ssize_t AMidiDevice_getNumOutputPorts(const AMidiDevice *device) {
     if (device == nullptr) {
         return AMEDIA_ERROR_INVALID_PARAMETER;
     }
@@ -291,7 +291,7 @@
 /*
  * Output (receiving) API
  */
-media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
+media_status_t AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
         AMidiOutputPort **outOutputPortPtr) {
     return AMIDI_openPort(device, portNumber, PORTTYPE_OUTPUT, (AMIDI_Port**)outOutputPortPtr);
 }
@@ -350,7 +350,7 @@
     AMIDI_Port *mPort;
 };
 
-ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
+ssize_t AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
          uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *timestampPtr) {
 
     if (outputPort == nullptr || buffer == nullptr) {
@@ -361,19 +361,19 @@
            numBytesReceivedPtr, timestampPtr);
 }
 
-void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) {
+void AMidiOutputPort_close(const AMidiOutputPort *outputPort) {
     AMIDI_closePort((AMIDI_Port*)outputPort);
 }
 
 /*
  * Input (sending) API
  */
-media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
+media_status_t AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
         AMidiInputPort **outInputPortPtr) {
     return AMIDI_openPort(device, portNumber, PORTTYPE_INPUT, (AMIDI_Port**)outInputPortPtr);
 }
 
-void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) {
+void AMidiInputPort_close(const AMidiInputPort *inputPort) {
     AMIDI_closePort((AMIDI_Port*)inputPort);
 }
 
@@ -386,12 +386,12 @@
     return numBytes + AMIDI_PACKET_OVERHEAD;
 }
 
-ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
+ssize_t AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
                             size_t numBytes) {
     return AMidiInputPort_sendWithTimestamp(inputPort, buffer, numBytes, 0);
 }
 
-ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
+ssize_t AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
         const uint8_t *data, size_t numBytes, int64_t timestamp) {
     if (inputPort == nullptr || data == nullptr) {
         return AMEDIA_ERROR_INVALID_PARAMETER;
@@ -423,7 +423,7 @@
     return numSent;
 }
 
-media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) {
+media_status_t AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) {
     if (inputPort == nullptr) {
         return AMEDIA_ERROR_INVALID_PARAMETER;
     }
diff --git a/media/native/midi/include/amidi/AMidi.h b/media/native/midi/include/amidi/AMidi.h
index cbe410f..76dec0f 100644
--- a/media/native/midi/include/amidi/AMidi.h
+++ b/media/native/midi/include/amidi/AMidi.h
@@ -41,8 +41,6 @@
 typedef struct AMidiInputPort AMidiInputPort;
 typedef struct AMidiOutputPort AMidiOutputPort;
 
-#define AMIDI_API __attribute__((visibility("default")))
-
 /*
  * Message Op Codes. Used to parse MIDI data packets
  */
@@ -61,6 +59,8 @@
     AMIDI_DEVICE_TYPE_BLUETOOTH = 3 /* A MIDI device connected via BlueTooth */
 };
 
+#if __ANDROID_API__ >= 29
+
 /*
  * Device API
  */
@@ -78,7 +78,7 @@
  *    is null or already connected to a native AMidiDevice
   *  @see AMEDIA_ERROR_UNKNOWN - an unknown error occurred.
  */
-media_status_t AMIDI_API AMidiDevice_fromJava(
+media_status_t AMidiDevice_fromJava(
         JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr) __INTRODUCED_IN(29);
 
 /**
@@ -93,7 +93,7 @@
  *  @see AMEDIA_ERROR_INVALID_OBJECT - the JNI interface initialization to the associated java MidiDevice failed.
  *  @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info.
  */
-media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice) __INTRODUCED_IN(29);
+media_status_t AMidiDevice_release(const AMidiDevice *midiDevice) __INTRODUCED_IN(29);
 
 /**
  * Gets the MIDI device type.
@@ -108,7 +108,7 @@
  *  @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL.
  *  @see AMEDIA_ERROR_UNKNOWN - Unknown error.
  */
-int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) __INTRODUCED_IN(29);
+int32_t AMidiDevice_getType(const AMidiDevice *device) __INTRODUCED_IN(29);
 
 /**
  * Gets the number of input (sending) ports available on the specified MIDI device.
@@ -120,7 +120,7 @@
  *  @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL.
  *  @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info.
  */
-ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
+ssize_t AMidiDevice_getNumInputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
 
 /**
  * Gets the number of output (receiving) ports available on the specified MIDI device.
@@ -132,7 +132,7 @@
  *  @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL.
  *  @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info.
  */
-ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
+ssize_t AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INTRODUCED_IN(29);
 
 /*
  * API for receiving data from the Output port of a device.
@@ -150,7 +150,7 @@
  * @return AMEDIA_OK, or a negative error code:
  *  @see AMEDIA_ERROR_UNKNOWN - Unknown Error.
  */
-media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
+media_status_t AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber,
                              AMidiOutputPort **outOutputPortPtr) __INTRODUCED_IN(29);
 
 /**
@@ -158,7 +158,7 @@
  *
  * @param outputPort    The native API port identifier of the port.
  */
-void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) __INTRODUCED_IN(29);
+void AMidiOutputPort_close(const AMidiOutputPort *outputPort) __INTRODUCED_IN(29);
 
 /**
  * Receives the next pending MIDI message. To retrieve all pending messages, the client should
@@ -178,7 +178,7 @@
  * @return the number of messages received (either 0 or 1), or a negative error code:
  *  @see AMEDIA_ERROR_UNKNOWN - Unknown Error.
  */
-ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
+ssize_t AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr,
          uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *outTimestampPtr) __INTRODUCED_IN(29);
 
 /*
@@ -197,7 +197,7 @@
  * @return AMEDIA_OK, or a negative error code:
  *  @see AMEDIA_ERROR_UNKNOWN - Unknown Error.
  */
-media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
+media_status_t AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber,
                             AMidiInputPort **outInputPortPtr) __INTRODUCED_IN(29);
 
 /**
@@ -210,7 +210,7 @@
  * @return The number of bytes sent, which could be less than specified or a negative error code:
  * @see AMEDIA_ERROR_INVALID_PARAMETER - The specified port was NULL, the specified buffer was NULL.
  */
-ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
+ssize_t AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer,
                    size_t numBytes) __INTRODUCED_IN(29);
 
 /**
@@ -224,7 +224,7 @@
  * @return The number of bytes sent, which could be less than specified or a negative error code:
  * @see AMEDIA_ERROR_INVALID_PARAMETER - The specified port was NULL, the specified buffer was NULL.
  */
-ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
+ssize_t AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort,
         const uint8_t *buffer, size_t numBytes, int64_t timestamp) __INTRODUCED_IN(29);
 
 /**
@@ -238,14 +238,16 @@
  * @see AMEDIA_ERROR_UNSUPPORTED - The FLUSH command couldn't
  * be sent.
  */
-media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
+media_status_t AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
 
 /**
  * Closes the input port.
  *
  * @param inputPort Identifies the input (sending) port to close.
  */
-void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
+void AMidiInputPort_close(const AMidiInputPort *inputPort) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
 
 #ifdef __cplusplus
 }
diff --git a/packages/CtsShim/build/Android.mk b/packages/CtsShim/build/Android.mk
index 03eb0d9..0ef4654 100644
--- a/packages/CtsShim/build/Android.mk
+++ b/packages/CtsShim/build/Android.mk
@@ -65,6 +65,9 @@
 
 LOCAL_MULTILIB := both
 LOCAL_JNI_SHARED_LIBRARIES := libshim_jni
+# Explicitly uncompress native libs rather than letting the build system doing it and destroy the
+# v2/v3 signature.
+LOCAL_USE_EMBEDDED_NATIVE_LIBS := true
 
 LOCAL_USE_AAPT2 := true
 
diff --git a/packages/DynamicSystemInstallationService/Android.bp b/packages/DynamicSystemInstallationService/Android.bp
new file mode 100644
index 0000000..f1a18ae
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/Android.bp
@@ -0,0 +1,14 @@
+android_app {
+    name: "DynamicSystemInstallationService",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    certificate: "platform",
+    privileged: true,
+    platform_apis: true,
+
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/packages/DynamicSystemInstallationService/Android.mk b/packages/DynamicSystemInstallationService/Android.mk
deleted file mode 100644
index 16aca1b..0000000
--- a/packages/DynamicSystemInstallationService/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, res)
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_PACKAGE_NAME := DynamicSystemInstallationService
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-
-include $(BUILD_PACKAGE)
diff --git a/packages/InputDevices/Android.bp b/packages/InputDevices/Android.bp
new file mode 100644
index 0000000..7532aea
--- /dev/null
+++ b/packages/InputDevices/Android.bp
@@ -0,0 +1,42 @@
+// Copyright (C) 2012 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.
+
+android_app {
+    name: "InputDevices",
+
+    srcs: [
+        "**/*.java",
+        ":validate_input_devices_keymaps",
+    ],
+
+    resource_dirs: ["res"],
+
+    sdk_version: "current",
+    certificate: "platform",
+    privileged: true,
+}
+
+// Validate all key maps.
+// Produces an empty srcjar that is used as an input to InputDevices to make sure
+// the check runs for platform builds.
+genrule {
+    name: "validate_input_devices_keymaps",
+    tools: [
+        "validatekeymaps",
+        "soong_zip",
+    ],
+    srcs: ["res/raw/*.kcm"],
+    out: ["validate_input_devices_keymaps.srcjar"],
+    cmd: "$(location validatekeymaps) -q $(in) && $(location soong_zip) -o $(out)",
+}
diff --git a/packages/InputDevices/Android.mk b/packages/InputDevices/Android.mk
deleted file mode 100644
index 80803fd..0000000
--- a/packages/InputDevices/Android.mk
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (C) 2012 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.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_JAVA_LIBRARIES :=
-
-LOCAL_PACKAGE_NAME := InputDevices
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-include $(BUILD_PACKAGE)
-
-# Validate all key maps.
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := validate_input_devices_keymaps
-intermediates := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE),,COMMON)
-LOCAL_BUILT_MODULE := $(intermediates)/stamp
-
-validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX)
-input_devices_keymaps := $(wildcard $(LOCAL_PATH)/res/raw/*.kcm)
-$(LOCAL_BUILT_MODULE): PRIVATE_VALIDATEKEYMAPS := $(validatekeymaps)
-$(LOCAL_BUILT_MODULE) : $(input_devices_keymaps) | $(validatekeymaps)
-	$(hide) $(PRIVATE_VALIDATEKEYMAPS) -q $^
-	$(hide) mkdir -p $(dir $@) && touch $@
-
-# Run validatekeymaps unconditionally for platform build.
-droidcore : $(LOCAL_BUILT_MODULE)
-
-# Reset temp vars.
-validatekeymaps :=
-input_devices_keymaps :=
diff --git a/packages/MtpDocumentsProvider/Android.bp b/packages/MtpDocumentsProvider/Android.bp
new file mode 100644
index 0000000..3dafa26
--- /dev/null
+++ b/packages/MtpDocumentsProvider/Android.bp
@@ -0,0 +1,11 @@
+android_app {
+    name: "MtpDocumentsProvider",
+
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "media",
+    privileged: true,
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
+}
diff --git a/packages/MtpDocumentsProvider/Android.mk b/packages/MtpDocumentsProvider/Android.mk
deleted file mode 100644
index 2d62a07..0000000
--- a/packages/MtpDocumentsProvider/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := MtpDocumentsProvider
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := media
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-# Only enable asserts on userdebug/eng builds
-ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
-LOCAL_JACK_FLAGS += -D jack.assert.policy=always
-endif
-
-include $(BUILD_PACKAGE)
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index beb1ac5..2734f4e 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -141,7 +141,7 @@
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicaciones eliminadas"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicaciones y usuarios eliminados"</string>
     <string name="data_usage_ota" msgid="5377889154805560860">"Actualizaciones del sistema"</string>
-    <string name="tether_settings_title_usb" msgid="6688416425801386511">"Conexión USB"</string>
+    <string name="tether_settings_title_usb" msgid="6688416425801386511">"Conexión a red por USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portátil"</string>
     <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Conexión Bluetooth"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Compartir conexión"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 238eba5..3a20d04 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -46,7 +46,7 @@
     <string name="wifi_limited_connection" msgid="7717855024753201527">"सीमित कनेक्शन"</string>
     <string name="wifi_status_no_internet" msgid="5784710974669608361">"इंटरनेट कनेक्शन नहीं है"</string>
     <string name="wifi_status_sign_in_required" msgid="123517180404752756">"साइन इन करना ज़रूरी है"</string>
-    <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"एक्सेस पॉइंट फ़िलहाल भरा हुआ है"</string>
+    <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ऐक्सेस पॉइंट फ़िलहाल भरा हुआ है"</string>
     <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s के ज़रिए कनेक्ट"</string>
     <string name="available_via_carrier" msgid="1469036129740799053">"%1$s के ज़रिए उपलब्ध"</string>
     <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> खोला जा रहा है"</string>
@@ -68,7 +68,7 @@
     <string name="bluetooth_pairing" msgid="1426882272690346242">"युग्‍मित कर रहा है…"</string>
     <string name="bluetooth_connected_no_headset" msgid="616068069034994802">"जुड़ गया (फ़ोन के ऑडियो को छोड़कर)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_a2dp" msgid="3736431800395923868">"जुड़ गया (मीडिया ऑडियो को छोड़कर)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_map" msgid="3200033913678466453">"जुड़ गया (मैसेज का एक्सेस नहीं)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_map" msgid="3200033913678466453">"जुड़ गया (मैसेज का ऐक्सेस नहीं)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2047403011284187056">"जुड़ गया (फ़ोन या मीडिया ऑडियो को छोड़कर)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_connected_battery_level" msgid="5162924691231307748">"जुड़ गया, बैटरी का लेवल <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_headset_battery_level" msgid="1610296229139400266">"जुड़ गया (फ़ोन के ऑडियो को छोड़कर), बैटरी का लेवल <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
@@ -88,7 +88,7 @@
     <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"संपर्क साझाकरण के लिए उपयोग करें"</string>
     <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"इंटरनेट कनेक्शन साझाकरण"</string>
     <string name="bluetooth_profile_map" msgid="1019763341565580450">"लेख संदेश"</string>
-    <string name="bluetooth_profile_sap" msgid="5764222021851283125">"सिम एक्सेस"</string>
+    <string name="bluetooth_profile_sap" msgid="5764222021851283125">"सिम ऐक्सेस"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ऑडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ऑडियो"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"सुनने में मदद करने वाले डिवाइस"</string>
@@ -200,7 +200,7 @@
     <string name="development_settings_not_available" msgid="4308569041701535607">"यह उपयोगकर्ता, डेवलपर के लिए सेटिंग और टूल का इस्तेमाल नहीं कर सकता"</string>
     <string name="vpn_settings_not_available" msgid="956841430176985598">"VPN सेटिंग इस उपयोगकर्ता के लिए उपलब्ध नहीं हैं"</string>
     <string name="tethering_settings_not_available" msgid="6765770438438291012">"टेदरिंग सेटिंग इस उपयोगकर्ता के लिए उपलब्ध नहीं हैं"</string>
-    <string name="apn_settings_not_available" msgid="7873729032165324000">"एक्सेस पॉइंट के नाम की सेटिंग इस उपयोगकर्ता के लिए मौजूद नहीं हैं"</string>
+    <string name="apn_settings_not_available" msgid="7873729032165324000">"ऐक्सेस पॉइंट के नाम की सेटिंग इस उपयोगकर्ता के लिए मौजूद नहीं हैं"</string>
     <string name="enable_adb" msgid="7982306934419797485">"USB डीबग करना"</string>
     <string name="enable_adb_summary" msgid="4881186971746056635">"डीबग मोड जब USB कनेक्‍ट किया गया हो"</string>
     <string name="clear_adb_keys" msgid="4038889221503122743">"USB डीबग करने की मंज़ूरी रद्द करें"</string>
@@ -414,7 +414,7 @@
     <string name="disabled" msgid="9206776641295849915">"बंद किया गया"</string>
     <string name="external_source_trusted" msgid="2707996266575928037">"अनुमति है"</string>
     <string name="external_source_untrusted" msgid="2677442511837596726">"अनुमति नहीं है"</string>
-    <string name="install_other_apps" msgid="6986686991775883017">"अनजान ऐप्लिकेशन इंस्टॉल करने का एक्सेस"</string>
+    <string name="install_other_apps" msgid="6986686991775883017">"अनजान ऐप्लिकेशन इंस्टॉल करने का ऐक्सेस"</string>
     <string name="home" msgid="3256884684164448244">"सेटिंग का होम पेज"</string>
   <string-array name="battery_labels">
     <item msgid="8494684293649631252">"0%"</item>
diff --git a/packages/SettingsLib/tests/Android.mk b/packages/SettingsLib/tests/Android.mk
deleted file mode 100644
index 333c41d..0000000
--- a/packages/SettingsLib/tests/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-# Include all makefiles in subdirectories
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index b11df48..7d56868 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -56,6 +56,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 import java.util.zip.CRC32;
 
 /**
@@ -241,6 +242,8 @@
         HashSet<String> movedToGlobal = new HashSet<String>();
         Settings.System.getMovedToGlobalSettings(movedToGlobal);
         Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
+        Set<String> movedToSecure = getMovedToSecureSettings();
+
         byte[] restoredWifiSupplicantData = null;
         byte[] restoredWifiIpConfigData = null;
 
@@ -259,16 +262,17 @@
 
             switch (key) {
                 case KEY_SYSTEM :
-                    restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal);
+                    restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal,
+                            movedToSecure);
                     mSettingsHelper.applyAudioSettings();
                     break;
 
                 case KEY_SECURE :
-                    restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal);
+                    restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal, null);
                     break;
 
                 case KEY_GLOBAL :
-                    restoreSettings(data, Settings.Global.CONTENT_URI, null);
+                    restoreSettings(data, Settings.Global.CONTENT_URI, null, movedToSecure);
                     break;
 
                 case KEY_WIFI_SUPPLICANT :
@@ -347,20 +351,22 @@
             HashSet<String> movedToGlobal = new HashSet<String>();
             Settings.System.getMovedToGlobalSettings(movedToGlobal);
             Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
+            Set<String> movedToSecure = getMovedToSecureSettings();
 
             // system settings data first
             int nBytes = in.readInt();
             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of settings data");
             byte[] buffer = new byte[nBytes];
             in.readFully(buffer, 0, nBytes);
-            restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal);
+            restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal,
+                    movedToSecure);
 
             // secure settings
             nBytes = in.readInt();
             if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data");
             if (nBytes > buffer.length) buffer = new byte[nBytes];
             in.readFully(buffer, 0, nBytes);
-            restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal);
+            restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal, null);
 
             // Global only if sufficiently new
             if (version >= FULL_BACKUP_ADDED_GLOBAL) {
@@ -369,7 +375,8 @@
                 if (nBytes > buffer.length) buffer = new byte[nBytes];
                 in.readFully(buffer, 0, nBytes);
                 movedToGlobal.clear();  // no redirection; this *is* the global namespace
-                restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal);
+                restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal,
+                        movedToSecure);
             }
 
             // locale
@@ -440,6 +447,13 @@
         }
     }
 
+    private Set<String> getMovedToSecureSettings() {
+        Set<String> movedToSecureSettings = new HashSet<>();
+        Settings.Global.getMovedToSecureSettings(movedToSecureSettings);
+        Settings.System.getMovedToSecureSettings(movedToSecureSettings);
+        return movedToSecureSettings;
+    }
+
     private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException {
         long[] stateChecksums = new long[STATE_SIZE];
 
@@ -564,7 +578,7 @@
     }
 
     private void restoreSettings(BackupDataInput data, Uri contentUri,
-            HashSet<String> movedToGlobal) {
+            HashSet<String> movedToGlobal, Set<String> movedToSecure) {
         byte[] settings = new byte[data.getDataSize()];
         try {
             data.readEntityData(settings, 0, settings.length);
@@ -572,11 +586,11 @@
             Log.e(TAG, "Couldn't read entity data");
             return;
         }
-        restoreSettings(settings, settings.length, contentUri, movedToGlobal);
+        restoreSettings(settings, settings.length, contentUri, movedToGlobal, movedToSecure);
     }
 
     private void restoreSettings(byte[] settings, int bytes, Uri contentUri,
-            HashSet<String> movedToGlobal) {
+            HashSet<String> movedToGlobal, Set<String> movedToSecure) {
         if (DEBUG) {
             Log.i(TAG, "restoreSettings: " + contentUri);
         }
@@ -651,9 +665,14 @@
                 continue;
             }
 
-            final Uri destination = (movedToGlobal != null && movedToGlobal.contains(key))
-                    ? Settings.Global.CONTENT_URI
-                    : contentUri;
+            final Uri destination;
+            if (movedToGlobal != null && movedToGlobal.contains(key)) {
+                destination = Settings.Global.CONTENT_URI;
+            } else if (movedToSecure != null && movedToSecure.contains(key)) {
+                destination = Settings.Secure.CONTENT_URI;
+            } else {
+                destination = contentUri;
+            }
             settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
                     mRestoredFromSdkInt);
 
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index c9bb83c..a7b4444 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -350,18 +350,19 @@
 
     private final class BugreportCallbackImpl extends BugreportCallback {
 
-        private final int mId;
         private final BugreportInfo mInfo;
 
-        BugreportCallbackImpl(String name, int id) {
-            mId = id;
+        BugreportCallbackImpl(String name) {
             // pid not used in this workflow, so setting default = 0
-            mInfo = new BugreportInfo(mContext, mId, 0 /* pid */, name,
+            mInfo = new BugreportInfo(mContext, 0 /* pid */, name,
                     100 /* max progress*/);
         }
 
         @Override
         public void onProgress(float progress) {
+            if (progress == 0) {
+                trackInfoWithId();
+            }
             checkProgressUpdated(mInfo, (int) progress);
         }
 
@@ -369,17 +370,34 @@
         // Logging errors and removing progress notification for now.
         @Override
         public void onError(@BugreportErrorCode int errorCode) {
-            stopProgress(mId);
+            trackInfoWithId();
+            stopProgress(mInfo.id);
             Log.e(TAG, "Bugreport API callback onError() errorCode = " + errorCode);
             return;
         }
 
         @Override
         public void onFinished() {
+            trackInfoWithId();
             // Stop running on foreground, otherwise share notification cannot be dismissed.
-            onBugreportFinished(mId);
+            onBugreportFinished(mInfo.id);
             stopSelfWhenDone();
         }
+
+        /**
+         * Reads bugreport id and links it to the bugreport info to track the bugreport's
+         * progress/completion/error. id is incremented in dumpstate code. This function is called
+         * when dumpstate calls one of the callback functions (onProgress, onFinished, onError)
+         * after the id has been incremented.
+         */
+        private void trackInfoWithId() {
+            final int id = SystemProperties.getInt(PROPERTY_LAST_ID, 1);
+            if (mBugreportInfos.get(id) == null) {
+                mInfo.id = id;
+                mBugreportInfos.put(mInfo.id, mInfo);
+            }
+            return;
+        }
     }
 
     /**
@@ -539,12 +557,17 @@
         }
     }
 
+    private String getBugreportName() {
+        String buildId = SystemProperties.get("ro.build.id", "UNKNOWN_BUILD");
+        String deviceName = SystemProperties.get("ro.product.name", "UNKNOWN_DEVICE");
+        String currentTimestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
+        return String.format("bugreport-%s-%s-%s", deviceName, buildId, currentTimestamp);
+    }
+
     private void startBugreportAPI(Intent intent) {
         mUsingBugreportApi = true;
-        String bugreportName = "bugreport-" + new SimpleDateFormat("yyyy-MM-dd-HH-mm").format(
-                new Date());
+        String bugreportName = getBugreportName();
 
-        // TODO(b/126862297): Make file naming same as dumpstate triggered bugreports
         ParcelFileDescriptor bugreportFd = createReadWriteFile(BUGREPORT_DIR,
                 bugreportName + ".zip");
         if (bugreportFd == null) {
@@ -565,19 +588,15 @@
         mBugreportManager = (BugreportManager) mContext.getSystemService(
                 Context.BUGREPORT_SERVICE);
         final Executor executor = ActivityThread.currentActivityThread().getExecutor();
-        // TODO(b/123617758): This id should come from dumpstate.
-        // Dumpstate increments PROPERTY_LAST_ID, may be racy if multiple calls
-        // to dumpstate are made simultaneously.
-        final int id = SystemProperties.getInt(PROPERTY_LAST_ID, 0) + 1;
+
         Log.i(TAG, "bugreport type = " + bugreportType
                 + " bugreport file fd: " + bugreportFd
                 + " screenshot file fd: " + screenshotFd);
 
-        BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName, id);
+        BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName);
         try {
             mBugreportManager.startBugreport(bugreportFd, screenshotFd,
                     new BugreportParams(bugreportType), executor, bugreportCallback);
-            mBugreportInfos.put(bugreportCallback.mInfo.id, bugreportCallback.mInfo);
         } catch (RuntimeException e) {
             Log.i(TAG, "error in generating bugreports: ", e);
             // The binder call didn't go through successfully, so need to close the fds.
@@ -1811,7 +1830,7 @@
         /**
          * Sequential, user-friendly id used to identify the bugreport.
          */
-        final int id;
+        int id;
 
         /**
          * {@code pid} of the {@code dumpstate} process generating the bugreport.
@@ -1903,8 +1922,15 @@
          * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
          */
         BugreportInfo(Context context, int id, int pid, String name, int max) {
-            this.context = context;
+            this(context, pid, name, max);
             this.id = id;
+        }
+
+        /**
+         * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED.
+         */
+        BugreportInfo(Context context, int pid, String name, int max) {
+            this.context = context;
             this.pid = pid;
             this.name = name;
             this.max = this.realMax = max;
diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp
new file mode 100644
index 0000000..ff26710
--- /dev/null
+++ b/packages/SimAppDialog/Android.bp
@@ -0,0 +1,15 @@
+android_app {
+    name: "SimAppDialog",
+
+    srcs: ["src/**/*.java"],
+
+    platform_apis: true,
+    certificate: "platform",
+
+    static_libs: [
+        "androidx.legacy_legacy-support-v4",
+        "setup-wizard-lib",
+    ],
+
+    resource_dirs: ["res"],
+}
diff --git a/packages/SimAppDialog/Android.mk b/packages/SimAppDialog/Android.mk
deleted file mode 100644
index 991e333..0000000
--- a/packages/SimAppDialog/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := SimAppDialog
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    androidx.legacy_legacy-support-v4
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-include frameworks/opt/setupwizard/library/common-platform-deprecated.mk
-
-include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/plugin/ExamplePlugin/Android.bp b/packages/SystemUI/plugin/ExamplePlugin/Android.bp
index a0eaf14..c6c80f3 100644
--- a/packages/SystemUI/plugin/ExamplePlugin/Android.bp
+++ b/packages/SystemUI/plugin/ExamplePlugin/Android.bp
@@ -11,4 +11,5 @@
 
     srcs: ["src/**/*.java"],
 
+    platform_apis: true,
 }
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index f124d89..6becd21 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -75,14 +75,15 @@
         <attr name="horizontalSpacing" format="dimension" />
     </declare-styleable>
 
-    <!-- Theme for icons in the status bar (light/dark). background/fillColor is used for dual tone
-         icons like wifi and signal, and singleToneColor is used for icons with only one tone.
+    <!-- Theme for icons in the status/nav bar (light/dark). background/fillColor is used for dual
+         tone icons like wifi and signal, and singleToneColor is used for icons with only one tone.
          Contract: Pixel with fillColor blended over backgroundColor blended over translucent should
          equal to singleToneColor blended over translucent. -->
     <declare-styleable name="TonedIcon">
         <attr name="backgroundColor" format="integer" />
         <attr name="fillColor" format="integer" />
         <attr name="singleToneColor" format="integer" />
+        <attr name="homeHandleColor" format="integer" />
     </declare-styleable>
 
     <declare-styleable name="StatusBarWindowView_Layout">
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index e7a1a66..61816f6 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -116,6 +116,9 @@
     <!-- The color of the navigation bar icons. Need to be in sync with ic_sysbar_* -->
     <color name="navigation_bar_icon_color">#E5FFFFFF</color>
 
+    <color name="navigation_bar_home_handle_light_color">#EBffffff</color>
+    <color name="navigation_bar_home_handle_dark_color">#99000000</color>
+
     <!-- The shadow color for light navigation bar icons. -->
     <color name="nav_key_button_shadow_color">#30000000</color>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2f1770a..6374191 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -366,11 +366,13 @@
         <item name="backgroundColor">@color/light_mode_icon_color_dual_tone_background</item>
         <item name="fillColor">@color/light_mode_icon_color_dual_tone_fill</item>
         <item name="singleToneColor">@color/light_mode_icon_color_single_tone</item>
+        <item name="homeHandleColor">@color/navigation_bar_home_handle_light_color</item>
     </style>
     <style name="DualToneDarkTheme">
         <item name="backgroundColor">@color/dark_mode_icon_color_dual_tone_background</item>
         <item name="fillColor">@color/dark_mode_icon_color_dual_tone_fill</item>
         <item name="singleToneColor">@color/dark_mode_icon_color_single_tone</item>
+        <item name="homeHandleColor">@color/navigation_bar_home_handle_dark_color</item>
     </style>
     <style name="QSHeaderDarkTheme">
         <item name="backgroundColor">@color/dark_mode_qs_icon_color_dual_tone_background</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 53403aa..a7e3c59 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -171,15 +171,21 @@
         PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
                 allowMultiple, mLooper, cls, this);
         p.loadAll();
-        mPluginMap.put(listener, p);
+        synchronized (this) {
+            mPluginMap.put(listener, p);
+        }
         startListening();
     }
 
     public void removePluginListener(PluginListener<?> listener) {
-        if (!mPluginMap.containsKey(listener)) return;
-        mPluginMap.remove(listener).destroy();
-        if (mPluginMap.size() == 0) {
-            stopListening();
+        synchronized (this) {
+            if (!mPluginMap.containsKey(listener)) {
+                return;
+            }
+            mPluginMap.remove(listener).destroy();
+            if (mPluginMap.size() == 0) {
+                stopListening();
+            }
         }
     }
 
@@ -208,8 +214,10 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
-            for (PluginInstanceManager manager : mPluginMap.values()) {
-                manager.loadAll();
+            synchronized (this) {
+                for (PluginInstanceManager manager : mPluginMap.values()) {
+                    manager.loadAll();
+                }
             }
         } else if (DISABLE_PLUGIN.equals(intent.getAction())) {
             Uri uri = intent.getData();
@@ -274,13 +282,15 @@
                     getPluginEnabler().setEnabled(componentName);
                 }
             }
-            if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
-                for (PluginInstanceManager manager : mPluginMap.values()) {
-                    manager.onPackageChange(pkg);
-                }
-            } else {
-                for (PluginInstanceManager manager : mPluginMap.values()) {
-                    manager.onPackageRemoved(pkg);
+            synchronized (this) {
+                if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+                    for (PluginInstanceManager manager : mPluginMap.values()) {
+                        manager.onPackageChange(pkg);
+                    }
+                } else {
+                    for (PluginInstanceManager manager : mPluginMap.values()) {
+                        manager.onPackageRemoved(pkg);
+                    }
                 }
             }
         }
@@ -322,9 +332,11 @@
     }
 
     public <T> boolean dependsOn(Plugin p, Class<T> cls) {
-        for (int i = 0; i < mPluginMap.size(); i++) {
-            if (mPluginMap.valueAt(i).dependsOn(p, cls)) {
-                return true;
+        synchronized (this) {
+            for (int i = 0; i < mPluginMap.size(); i++) {
+                if (mPluginMap.valueAt(i).dependsOn(p, cls)) {
+                    return true;
+                }
             }
         }
         return false;
@@ -335,10 +347,12 @@
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println(String.format("  plugin map (%d):", mPluginMap.size()));
-        for (PluginListener listener: mPluginMap.keySet()) {
-            pw.println(String.format("    %s -> %s",
-                    listener, mPluginMap.get(listener)));
+        synchronized (this) {
+            pw.println(String.format("  plugin map (%d):", mPluginMap.size()));
+            for (PluginListener listener : mPluginMap.keySet()) {
+                pw.println(String.format("    %s -> %s",
+                        listener, mPluginMap.get(listener)));
+            }
         }
     }
 
@@ -418,8 +432,10 @@
                 // We couldn't find any plugins involved in this crash, just to be safe
                 // disable all the plugins, so we can be sure that SysUI is running as
                 // best as possible.
-                for (PluginInstanceManager manager : mPluginMap.values()) {
-                    disabledAny |= manager.disableAll();
+                synchronized (this) {
+                    for (PluginInstanceManager manager : mPluginMap.values()) {
+                        disabledAny |= manager.disableAll();
+                    }
                 }
             }
             if (disabledAny) {
@@ -433,9 +449,11 @@
         private boolean checkStack(Throwable throwable) {
             if (throwable == null) return false;
             boolean disabledAny = false;
-            for (StackTraceElement element : throwable.getStackTrace()) {
-                for (PluginInstanceManager manager : mPluginMap.values()) {
-                    disabledAny |= manager.checkAndDisable(element.getClassName());
+            synchronized (this) {
+                for (StackTraceElement element : throwable.getStackTrace()) {
+                    for (PluginInstanceManager manager : mPluginMap.values()) {
+                        disabledAny |= manager.checkAndDisable(element.getClassName());
+                    }
                 }
             }
             return disabledAny | checkStack(throwable.getCause());
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java
index b2c79a4..e106c65 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java
@@ -52,7 +52,11 @@
                 if (mEvictionCallback != null) {
                     mEvictionCallback.onEntryEvicted(mKeys.get(taskId));
                 }
-                mKeys.remove(taskId);
+
+                // Only remove from mKeys on cache remove, not a cache update.
+                if (newV == null) {
+                    mKeys.remove(taskId);
+                }
             }
         };
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index cc7863c..c732584 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -80,18 +80,25 @@
     public static final int SYSUI_STATE_HOME_DISABLED = 1 << 8;
     // The keyguard is showing, but occluded
     public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED = 1 << 9;
+    // The search feature is disabled (either by SUW/SysUI/device policy)
+    public static final int SYSUI_STATE_SEARCH_DISABLED = 1 << 10;
+    // The notification panel is expanded and interactive (either locked or unlocked), and the
+    // quick settings is not expanded
+    public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
             SYSUI_STATE_NAV_BAR_HIDDEN,
             SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
+            SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
             SYSUI_STATE_BOUNCER_SHOWING,
             SYSUI_STATE_A11Y_BUTTON_CLICKABLE,
             SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE,
             SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
             SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
             SYSUI_STATE_OVERVIEW_DISABLED,
-            SYSUI_STATE_HOME_DISABLED
+            SYSUI_STATE_HOME_DISABLED,
+            SYSUI_STATE_SEARCH_DISABLED
     })
     public @interface SystemUiStateFlags {}
 
@@ -100,8 +107,10 @@
         str.add((flags & SYSUI_STATE_SCREEN_PINNING) != 0 ? "screen_pinned" : "");
         str.add((flags & SYSUI_STATE_OVERVIEW_DISABLED) != 0 ? "overview_disabled" : "");
         str.add((flags & SYSUI_STATE_HOME_DISABLED) != 0 ? "home_disabled" : "");
+        str.add((flags & SYSUI_STATE_SEARCH_DISABLED) != 0 ? "search_disabled" : "");
         str.add((flags & SYSUI_STATE_NAV_BAR_HIDDEN) != 0 ? "navbar_hidden" : "");
         str.add((flags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0 ? "notif_visible" : "");
+        str.add((flags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) != 0 ? "qs_visible" : "");
         str.add((flags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING) != 0 ? "keygrd_visible" : "");
         str.add((flags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0
                 ? "keygrd_occluded" : "");
@@ -150,10 +159,13 @@
      * disabled.
      */
     public static boolean isAssistantGestureDisabled(int sysuiStateFlags) {
-        // Disable when in screen pinning, immersive, the bouncer is showing
+        // Disable when in quick settings, screen pinning, immersive, the bouncer is showing, 
+        // or search is disabled
         int disableFlags = SYSUI_STATE_SCREEN_PINNING
                 | SYSUI_STATE_NAV_BAR_HIDDEN
-                | SYSUI_STATE_BOUNCER_SHOWING;
+                | SYSUI_STATE_BOUNCER_SHOWING
+                | SYSUI_STATE_SEARCH_DISABLED
+                | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
         if ((sysuiStateFlags & disableFlags) != 0) {
             return true;
         }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentTaskInfoCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentTaskInfoCompat.java
deleted file mode 100644
index a529903..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentTaskInfoCompat.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.shared.system;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-
-public class RecentTaskInfoCompat {
-
-    private ActivityManager.RecentTaskInfo mInfo;
-
-    public RecentTaskInfoCompat(ActivityManager.RecentTaskInfo info) {
-        mInfo = info;
-    }
-
-    public int getUserId() {
-        return mInfo.userId;
-    }
-
-    public boolean supportsSplitScreenMultiWindow() {
-        return mInfo.supportsSplitScreenMultiWindow;
-    }
-
-    public ComponentName getTopActivity() {
-        return mInfo.topActivity;
-    }
-
-    public ActivityManager.TaskDescription getTaskDescription() {
-        return mInfo.taskDescription;
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index d2fe5cd..2ef0422 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -91,6 +91,7 @@
         }
     }
 
+    @Deprecated
     public void setCancelWithDeferredScreenshot(boolean screenshot) {
         try {
             mAnimationController.setCancelWithDeferredScreenshot(screenshot);
@@ -99,6 +100,14 @@
         }
     }
 
+    public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
+        try {
+            mAnimationController.setDeferCancelUntilNextTransition(defer, screenshot);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to set deferred cancel with screenshot", e);
+        }
+    }
+
     public void cleanupScreenshot() {
         try {
             mAnimationController.cleanupScreenshot();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java
new file mode 100644
index 0000000..326c2aa
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.content.ComponentName;
+
+public class TaskInfoCompat {
+
+    public static int getUserId(TaskInfo info) {
+        return info.userId;
+    }
+
+    public static int getActivityType(TaskInfo info) {
+        return info.configuration.windowConfiguration.getActivityType();
+    }
+
+    public static int getWindowingMode(TaskInfo info) {
+        return info.configuration.windowConfiguration.getWindowingMode();
+    }
+
+    public static boolean supportsSplitScreenMultiWindow(TaskInfo info) {
+        return info.supportsSplitScreenMultiWindow;
+    }
+
+    public static ComponentName getTopActivity(TaskInfo info) {
+        return info.topActivity;
+    }
+
+    public static ActivityManager.TaskDescription getTaskDescription(TaskInfo info) {
+        return info.taskDescription;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 2483192..5097216 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -584,6 +584,9 @@
         @Override
         public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
                 TransitionValues endValues) {
+            if (!sceneRoot.isShown()) {
+                return null;
+            }
             final float cutoff = mCutoff;
             final int startVisibility = View.INVISIBLE;
             final int endVisibility = (int) endValues.values.get(PROPNAME_VISIBILITY);
@@ -596,6 +599,9 @@
         @Override
         public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
                 TransitionValues endValues) {
+            if (!sceneRoot.isShown()) {
+                return null;
+            }
             final float cutoff = 1f - mCutoff;
             final int startVisibility = View.VISIBLE;
             final int endVisibility = (int) endValues.values.get(PROPNAME_VISIBILITY);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 4ad262f..8059dcf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -269,7 +269,7 @@
      */
     private void updateBiometricRetry() {
         SecurityMode securityMode = getSecurityMode();
-        mSwipeUpToRetry = mUnlockMethodCache.isUnlockingWithFacePossible()
+        mSwipeUpToRetry = mUnlockMethodCache.isFaceAuthEnabled()
                 && securityMode != SecurityMode.SimPin
                 && securityMode != SecurityMode.SimPuk
                 && securityMode != SecurityMode.None;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index e219e24..af4e61b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -89,6 +89,7 @@
     private final HashMap<View, PendingIntent> mClickActions;
     private final ActivityStarter mActivityStarter;
     private final ConfigurationController mConfigurationController;
+    private final LayoutTransition mLayoutTransition;
     private Uri mKeyguardSliceUri;
     @VisibleForTesting
     TextView mTitle;
@@ -126,16 +127,16 @@
         mActivityStarter = activityStarter;
         mConfigurationController = configurationController;
 
-        LayoutTransition transition = new LayoutTransition();
-        transition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2);
-        transition.setDuration(LayoutTransition.APPEARING, DEFAULT_ANIM_DURATION);
-        transition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 2);
-        transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
-        transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
-        transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.FAST_OUT_SLOW_IN);
-        transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
-        transition.setAnimateParentHierarchy(false);
-        setLayoutTransition(transition);
+        mLayoutTransition = new LayoutTransition();
+        mLayoutTransition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2);
+        mLayoutTransition.setDuration(LayoutTransition.APPEARING, DEFAULT_ANIM_DURATION);
+        mLayoutTransition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 2);
+        mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
+        mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+        mLayoutTransition.setInterpolator(LayoutTransition.APPEARING,
+                Interpolators.FAST_OUT_SLOW_IN);
+        mLayoutTransition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
+        mLayoutTransition.setAnimateParentHierarchy(false);
     }
 
     @Override
@@ -174,6 +175,12 @@
         mConfigurationController.removeCallback(this);
     }
 
+    @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        super.onVisibilityAggregated(isVisible);
+        setLayoutTransition(isVisible ? mLayoutTransition : null);
+    }
+
     /**
      * Returns whether the current visible slice has a title/header.
      */
@@ -419,6 +426,7 @@
          * their desired positions.
          */
         private final Animation.AnimationListener mKeepAwakeListener;
+        private LayoutTransition mLayoutTransition;
         private float mDarkAmount;
 
         public Row(Context context) {
@@ -440,33 +448,41 @@
 
         @Override
         protected void onFinishInflate() {
-            LayoutTransition transition = new LayoutTransition();
-            transition.setDuration(DEFAULT_ANIM_DURATION);
+            mLayoutTransition = new LayoutTransition();
+            mLayoutTransition.setDuration(DEFAULT_ANIM_DURATION);
 
             PropertyValuesHolder left = PropertyValuesHolder.ofInt("left", 0, 1);
             PropertyValuesHolder right = PropertyValuesHolder.ofInt("right", 0, 1);
             ObjectAnimator changeAnimator = ObjectAnimator.ofPropertyValuesHolder((Object) null,
                     left, right);
-            transition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeAnimator);
-            transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeAnimator);
-            transition.setInterpolator(LayoutTransition.CHANGE_APPEARING,
+            mLayoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeAnimator);
+            mLayoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeAnimator);
+            mLayoutTransition.setInterpolator(LayoutTransition.CHANGE_APPEARING,
                     Interpolators.ACCELERATE_DECELERATE);
-            transition.setInterpolator(LayoutTransition.CHANGE_DISAPPEARING,
+            mLayoutTransition.setInterpolator(LayoutTransition.CHANGE_DISAPPEARING,
                     Interpolators.ACCELERATE_DECELERATE);
-            transition.setStartDelay(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION);
-            transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, DEFAULT_ANIM_DURATION);
+            mLayoutTransition.setStartDelay(LayoutTransition.CHANGE_APPEARING,
+                    DEFAULT_ANIM_DURATION);
+            mLayoutTransition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING,
+                    DEFAULT_ANIM_DURATION);
 
             ObjectAnimator appearAnimator = ObjectAnimator.ofFloat(null, "alpha", 0f, 1f);
-            transition.setAnimator(LayoutTransition.APPEARING, appearAnimator);
-            transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.ALPHA_IN);
+            mLayoutTransition.setAnimator(LayoutTransition.APPEARING, appearAnimator);
+            mLayoutTransition.setInterpolator(LayoutTransition.APPEARING, Interpolators.ALPHA_IN);
 
             ObjectAnimator disappearAnimator = ObjectAnimator.ofFloat(null, "alpha", 1f, 0f);
-            transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT);
-            transition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 4);
-            transition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnimator);
+            mLayoutTransition.setInterpolator(LayoutTransition.DISAPPEARING,
+                    Interpolators.ALPHA_OUT);
+            mLayoutTransition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 4);
+            mLayoutTransition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnimator);
 
-            transition.setAnimateParentHierarchy(false);
-            setLayoutTransition(transition);
+            mLayoutTransition.setAnimateParentHierarchy(false);
+        }
+
+        @Override
+        public void onVisibilityAggregated(boolean isVisible) {
+            super.onVisibilityAggregated(isVisible);
+            setLayoutTransition(isVisible ? mLayoutTransition : null);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index fd618b0..4e7b157 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1783,13 +1783,15 @@
                 && mFpm.getEnrolledFingerprints(userId).size() > 0;
     }
 
+    private boolean isUnlockWithFacePossible(int userId) {
+        return isFaceAuthEnabledForUser(userId) && !isFaceDisabled(userId);
+    }
+
     /**
      * If face hardware is available, user has enrolled and enabled auth via setting.
-     * Not considering encryption or lock down state.
      */
-    public boolean isUnlockWithFacePossible(int userId) {
+    public boolean isFaceAuthEnabledForUser(int userId) {
         return mFaceManager != null && mFaceManager.isHardwareDetected()
-                && !isFaceDisabled(userId)
                 && mFaceManager.hasEnrolledTemplates(userId)
                 && mFaceSettingEnabledForUser.get(userId);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
index 54a3635..a94952c 100644
--- a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
@@ -107,7 +107,9 @@
         mPaint.setColor((int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
                 mLightColor,
                 mDarkColor));
-        invalidate();
+        if (getVisibility() == VISIBLE) {
+            invalidate();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/DumpController.kt b/packages/SystemUI/src/com/android/systemui/DumpController.kt
index 646abb5..65f1abd 100644
--- a/packages/SystemUI/src/com/android/systemui/DumpController.kt
+++ b/packages/SystemUI/src/com/android/systemui/DumpController.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui
 
+import android.util.ArraySet
 import android.util.Log
 import androidx.annotation.GuardedBy
 import com.android.internal.util.Preconditions
@@ -39,38 +40,39 @@
     }
 
     @GuardedBy("listeners")
-    private val listeners = mutableListOf<WeakReference<Dumpable>>()
+    private val listeners = mutableListOf<RegisteredDumpable>()
     val numListeners: Int
         get() = listeners.size
 
     /**
-     * Adds a [Dumpable] listener to be dumped. It will only be added if it is not already tracked.
+     * Adds a [Dumpable] dumpable to be dumped.
      *
-     * @param listener the [Dumpable] to be added
+     * @param tag a string tag to associate with this dumpable. Tags must be globally unique; this
+     *      method will throw if the same tag has already been registered. Tags can be used to
+     *      filter output when debugging.
+     * @param dumpable the [Dumpable] to be added
      */
-    fun addListener(listener: Dumpable) {
-        Preconditions.checkNotNull(listener, "The listener to be added cannot be null")
-        if (DEBUG) Log.v(TAG, "*** register callback for $listener")
+    fun registerDumpable(tag: String, dumpable: Dumpable) {
+        Preconditions.checkNotNull(dumpable, "The dumpable to be added cannot be null")
+        if (DEBUG) Log.v(TAG, "*** register callback for $dumpable")
         synchronized<Unit>(listeners) {
-            if (listeners.any { it.get() == listener }) {
-                if (DEBUG) {
-                    Log.e(TAG, "Object tried to add another callback")
-                }
+            if (listeners.any { it.tag == tag }) {
+                throw IllegalArgumentException("Duplicate dumpable tag registered: $tag")
             } else {
-                listeners.add(WeakReference(listener))
+                listeners.add(RegisteredDumpable(tag, WeakReference(dumpable)))
             }
         }
     }
 
     /**
-     * Removes a listener from the list of elements to be dumped.
+     * Removes a dumpable from the list of elements to be dumped.
      *
-     * @param listener the [Dumpable] to be removed.
+     * @param dumpable the [Dumpable] to be removed.
      */
-    fun removeListener(listener: Dumpable) {
-        if (DEBUG) Log.v(TAG, "*** unregister callback for $listener")
+    fun unregisterDumpable(dumpable: Dumpable) {
+        if (DEBUG) Log.v(TAG, "*** unregister callback for $dumpable")
         synchronized(listeners) {
-            listeners.removeAll { it.get() == listener || it.get() == null }
+            listeners.removeAll { it.dumpable.get() == dumpable || it.dumpable.get() == null }
         }
     }
 
@@ -79,8 +81,22 @@
      */
     override fun dump(fd: FileDescriptor?, pw: PrintWriter, args: Array<String>?) {
         pw.println("DumpController state:")
+
+        val filter = if (args != null && args.size >= 3 &&
+                args[0] == "dependency" && args[1] == "DumpController") {
+            ArraySet(args[2].split(',').map { it.toLowerCase() })
+        } else {
+            null
+        }
+
         synchronized(listeners) {
-            listeners.forEach { it.get()?.dump(fd, pw, args) }
+            listeners.forEach {
+                if (filter == null || filter.contains(it.tag.toLowerCase())) {
+                    it.dumpable.get()?.dump(fd, pw, args)
+                }
+            }
         }
     }
+
+    data class RegisteredDumpable(val tag: String, val dumpable: WeakReference<Dumpable>)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index c9d4957..bd91333 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -47,7 +47,7 @@
     // This is to avoid destroying then recreating render context in a very short time.
     private static final int DELAY_FINISH_RENDERING = 1000;
     private static final int INTERVAL_WAIT_FOR_RENDERING = 100;
-    private static final int PATIENCE_WAIT_FOR_RENDERING = 5;
+    private static final int PATIENCE_WAIT_FOR_RENDERING = 10;
     private HandlerThread mWorker;
 
     @Override
@@ -124,10 +124,10 @@
 
         @Override
         public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
-            long duration = mNeedTransition || animationDuration != 0 ? animationDuration : 0;
+            if (!mNeedTransition) return;
             mWorker.getThreadHandler().post(
-                    () -> mRenderer.updateAmbientMode(inAmbientMode, duration));
-            if (inAmbientMode && duration == 0) {
+                    () -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration));
+            if (inAmbientMode && animationDuration == 0) {
                 // This means that we are transiting from home to aod, to avoid
                 // race condition between window visibility and transition,
                 // we don't return until the transition is finished. See b/136643341.
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
index 002d4f3..7595654 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
@@ -127,7 +127,7 @@
                                 SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE, null));
                     }
                 });
-        Dependency.get(DumpController.class).addListener(this);
+        Dependency.get(DumpController.class).registerDumpable(TAG, this);
     }
 
     @Override // AssistHandleCallbacks
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index a5857df..ce67577 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -111,6 +111,9 @@
     protected boolean mRequireConfirmation;
     private int mUserId; // used to determine if we should show work background
 
+    private boolean mCompletedAnimatingIn;
+    private boolean mPendingDismissDialog;
+
     protected abstract int getHintStringResourceId();
     protected abstract int getAuthenticatedAccessibilityResourceId();
     protected abstract int getIconDescriptionResourceId();
@@ -332,6 +335,7 @@
             mDialog.setAlpha(1.0f);
             mDialog.setTranslationY(0);
             mLayout.setAlpha(1.0f);
+            mCompletedAnimatingIn = true;
         } else {
             // Dim the background and slide the dialog up
             mDialog.setTranslationY(mAnimationTranslationOffset);
@@ -352,6 +356,12 @@
     }
 
     public void startDismiss() {
+        if (!mCompletedAnimatingIn) {
+            Log.w(TAG, "startDismiss(): waiting for onDialogAnimatedIn");
+            mPendingDismissDialog = true;
+            return;
+        }
+
         mAnimatingAway = true;
 
         // This is where final cleanup should occur.
@@ -499,6 +509,13 @@
     }
 
     public void onDialogAnimatedIn() {
+        mCompletedAnimatingIn = true;
+
+        if (mPendingDismissDialog) {
+            Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now");
+            startDismiss();
+            mPendingDismissDialog = false;
+        }
     }
 
     public void restoreState(Bundle bundle) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
index 729242e..ae6cb5c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
@@ -460,6 +460,7 @@
 
     @Override
     public void onDialogAnimatedIn() {
+        super.onDialogAnimatedIn();
         mDialogAnimatedIn = true;
         mIconController.startPulsing();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index cee01a4..ce82bbf 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -50,6 +50,8 @@
     private boolean mSessionStarted;
     private MetricsLogger mMetricsLogger;
     private int mIsFalseTouchCalls;
+    private boolean mShowingAod;
+    private boolean mScreenOn;
 
     private final ExecutorService mBackgroundExecutor = Executors.newSingleThreadExecutor();
 
@@ -105,10 +107,12 @@
     }
 
     private void sessionStart() {
-        logDebug("Starting Session");
-        mSessionStarted = true;
-        registerSensors();
-        mClassifiers.forEach(FalsingClassifier::onSessionStarted);
+        if (!mSessionStarted && !mShowingAod && mScreenOn) {
+            logDebug("Starting Session");
+            mSessionStarted = true;
+            registerSensors();
+            mClassifiers.forEach(FalsingClassifier::onSessionStarted);
+        }
     }
 
     private void sessionEnd() {
@@ -172,6 +176,7 @@
             mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls);
             mIsFalseTouchCalls = 0;
         }
+        sessionEnd();
     }
 
     @Override
@@ -180,6 +185,7 @@
 
     @Override
     public void setShowingAod(boolean showingAod) {
+        mShowingAod = showingAod;
         if (showingAod) {
             sessionEnd();
         } else {
@@ -264,7 +270,7 @@
 
     @Override
     public void onScreenOnFromTouch() {
-        sessionStart();
+        onScreenTurningOn();
     }
 
     @Override
@@ -286,11 +292,13 @@
 
     @Override
     public void onScreenTurningOn() {
+        mScreenOn = true;
         sessionStart();
     }
 
     @Override
     public void onScreenOff() {
+        mScreenOn = false;
         sessionEnd();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index ae6dac5..07dd2cd 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -77,8 +77,10 @@
     interface Callback {
         /**
          * Called when a high priority notification is added.
+         * @param onPulseSuppressedListener A listener that is invoked if the pulse is being
+         *                                  supressed.
          */
-        default void onNotificationAlerted() {}
+        default void onNotificationAlerted(Runnable onPulseSuppressedListener) {}
 
         /**
          * Called when battery state or power save mode changes.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 8ef01e8..2ca85c0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -106,22 +106,31 @@
         mDockManager = dockManager;
     }
 
-    private void onNotification() {
+    private void onNotification(Runnable onPulseSuppressedListener) {
         if (DozeMachine.DEBUG) {
             Log.d(TAG, "requestNotificationPulse");
         }
         if (!sWakeDisplaySensorState) {
             Log.d(TAG, "Wake display false. Pulse denied.");
+            runIfNotNull(onPulseSuppressedListener);
             return;
         }
         mNotificationPulseTime = SystemClock.elapsedRealtime();
         if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) {
+            runIfNotNull(onPulseSuppressedListener);
             return;
         }
-        requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */);
+        requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */,
+                onPulseSuppressedListener);
         DozeLog.traceNotificationPulse(mContext);
     }
 
+    private static void runIfNotNull(Runnable runnable) {
+        if (runnable != null) {
+            runnable.run();
+        }
+    }
+
     private void proximityCheckThenCall(IntConsumer callback,
             boolean alreadyPerformedProxCheck,
             int reason) {
@@ -158,10 +167,11 @@
         if (isWakeDisplay) {
             onWakeScreen(wakeEvent, mMachine.isExecutingTransition() ? null : mMachine.getState());
         } else if (isLongPress) {
-            requestPulse(pulseReason, sensorPerformedProxCheck);
+            requestPulse(pulseReason, sensorPerformedProxCheck, null /* onPulseSupressedListener */);
         } else if (isWakeLockScreen) {
             if (wakeEvent) {
-                requestPulse(pulseReason, sensorPerformedProxCheck);
+                requestPulse(pulseReason, sensorPerformedProxCheck,
+                        null /* onPulseSupressedListener */);
             }
         } else {
             proximityCheckThenCall((result) -> {
@@ -340,7 +350,8 @@
         }
     }
 
-    private void requestPulse(final int reason, boolean performedProxCheck) {
+    private void requestPulse(final int reason, boolean performedProxCheck,
+            Runnable onPulseSuppressedListener) {
         Assert.isMainThread();
         mDozeHost.extendPulse(reason);
 
@@ -357,6 +368,7 @@
                 DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(),
                         mDozeHost.isPulsingBlocked());
             }
+            runIfNotNull(onPulseSuppressedListener);
             return;
         }
 
@@ -365,6 +377,7 @@
             if (result == ProximityCheck.RESULT_NEAR) {
                 // in pocket, abort pulse
                 mPulsePending = false;
+                runIfNotNull(onPulseSuppressedListener);
             } else {
                 // not in pocket, continue pulsing
                 continuePulseRequest(reason);
@@ -482,7 +495,8 @@
         public void onReceive(Context context, Intent intent) {
             if (PULSE_ACTION.equals(intent.getAction())) {
                 if (DozeMachine.DEBUG) Log.d(TAG, "Received pulse intent");
-                requestPulse(DozeLog.PULSE_REASON_INTENT, false /* performedProxCheck */);
+                requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */
+                        null /* onPulseSupressedListener */);
             }
             if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
                 mMachine.requestState(DozeMachine.State.FINISH);
@@ -532,8 +546,8 @@
 
     private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
         @Override
-        public void onNotificationAlerted() {
-            onNotification();
+        public void onNotificationAlerted(Runnable onPulseSuppressedListener) {
+            onNotification(onPulseSuppressedListener);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
index 45e97b3..b154e66 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
@@ -65,7 +65,7 @@
             @Override
             public void onAnimationStart(Animator animation) {
                 if (mRevealListener != null) {
-                    mRevealListener.onRevealStart();
+                    mRevealListener.onRevealStart(true /* animate */);
                 }
             }
         });
@@ -73,7 +73,7 @@
 
     private void animate() {
         mAnimator.cancel();
-        mAnimator.setFloatValues(mReveal, !mAwake ? MIN_REVEAL : MAX_REVEAL);
+        mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL);
         mAnimator.start();
     }
 
@@ -84,12 +84,11 @@
     void updateAwake(boolean awake, long duration) {
         mAwake = awake;
         mAnimator.setDuration(duration);
-        if (!mAwake && duration == 0) {
-            // We are transiting from home to aod,
-            // since main thread is waiting for rendering finished, we only need draw
-            // the last state directly, which is a black screen.
-            mReveal = MIN_REVEAL;
-            mRevealListener.onRevealStart();
+        if (duration == 0) {
+            // We are transiting from home to aod or aod to home directly,
+            // we don't need to do transition in these cases.
+            mReveal = mAwake ? MAX_REVEAL : MIN_REVEAL;
+            mRevealListener.onRevealStart(false /* animate */);
             mRevealListener.onRevealStateChanged();
             mRevealListener.onRevealEnd();
         } else {
@@ -110,7 +109,7 @@
         /**
          * Called back while reveal starts.
          */
-        void onRevealStart();
+        void onRevealStart(boolean animate);
 
         /**
          * Called back while reveal ends.
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 93d8dd6..7b22a49 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -24,6 +24,7 @@
 
 import android.app.WallpaperManager;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.util.Log;
@@ -70,7 +71,14 @@
         DisplayInfo displayInfo = new DisplayInfo();
         WindowManager wm = context.getSystemService(WindowManager.class);
         wm.getDefaultDisplay().getDisplayInfo(displayInfo);
-        mScissor = new Rect(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+
+        // We only do transition in portrait currently, b/137962047.
+        int orientation = context.getResources().getConfiguration().orientation;
+        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+            mScissor = new Rect(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+        } else {
+            mScissor = new Rect(0, 0, displayInfo.logicalHeight, displayInfo.logicalWidth);
+        }
 
         mProxy = proxy;
         mProgram = new ImageGLProgram(context);
@@ -179,20 +187,24 @@
     }
 
     @Override
-    public void onRevealStart() {
-        mScissorMode = true;
-        // Use current display area of texture.
-        mWallpaper.adjustTextureCoordinates(mSurfaceSize, mScissor, mXOffset, mYOffset);
+    public void onRevealStart(boolean animate) {
+        if (animate) {
+            mScissorMode = true;
+            // Use current display area of texture.
+            mWallpaper.adjustTextureCoordinates(mSurfaceSize, mScissor, mXOffset, mYOffset);
+        }
         mProxy.preRender();
     }
 
     @Override
     public void onRevealEnd() {
-        mScissorMode = false;
-        // reset texture coordinates to use full texture.
-        mWallpaper.adjustTextureCoordinates(null, null, 0, 0);
-        // We need draw full texture back before finishing render.
-        mProxy.requestRender();
+        if (mScissorMode) {
+            mScissorMode = false;
+            // reset texture coordinates to use full texture.
+            mWallpaper.adjustTextureCoordinates(null, null, 0, 0);
+            // We need draw full texture back before finishing render.
+            mProxy.requestRender();
+        }
         mProxy.postRender();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 5136682..48f32cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -52,6 +52,7 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.NextAlarmController;
@@ -62,7 +63,6 @@
 import com.android.systemui.util.wakelock.WakeLock;
 
 import java.util.Date;
-import java.util.HashSet;
 import java.util.Locale;
 import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
@@ -128,6 +128,7 @@
     private CharSequence mMediaTitle;
     private CharSequence mMediaArtist;
     protected boolean mDozing;
+    private int mStatusBarState;
     private boolean mMediaIsVisible;
 
     /**
@@ -231,7 +232,11 @@
     protected boolean needsMediaLocked() {
         boolean keepWhenAwake = mKeyguardBypassController != null
                 && mKeyguardBypassController.getBypassEnabled() && mDozeParameters.getAlwaysOn();
-        return !TextUtils.isEmpty(mMediaTitle) && mMediaIsVisible && (mDozing || keepWhenAwake);
+        // Show header if music is playing and the status bar is in the shade state. This way, an
+        // animation isn't necessary when pressing power and transitioning to AOD.
+        boolean keepWhenShade = mStatusBarState == StatusBarState.SHADE && mMediaIsVisible;
+        return !TextUtils.isEmpty(mMediaTitle) && mMediaIsVisible && (mDozing || keepWhenAwake
+                || keepWhenShade);
     }
 
     protected void addMediaLocked(ListBuilder listBuilder) {
@@ -458,7 +463,7 @@
         synchronized (this) {
             boolean nextVisible = NotificationMediaManager.isPlayingState(state);
             mHandler.removeCallbacksAndMessages(mMediaToken);
-            if (mMediaIsVisible && !nextVisible) {
+            if (mMediaIsVisible && !nextVisible && mStatusBarState != StatusBarState.SHADE) {
                 // We need to delay this event for a few millis when stopping to avoid jank in the
                 // animation. The media app might not send its update when buffering, and the slice
                 // would end up without a header for 0.5 second.
@@ -515,5 +520,14 @@
 
     @Override
     public void onStateChanged(int newState) {
+        final boolean notify;
+        synchronized (this) {
+            boolean needsMedia = needsMediaLocked();
+            mStatusBarState = newState;
+            notify = needsMedia != needsMediaLocked();
+        }
+        if (notify) {
+            notifyChange();
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 89aa96d..ae83567 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -187,7 +187,7 @@
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.addCallback(this);
         }
-        if (mDumpController != null) mDumpController.addListener(this);
+        if (mDumpController != null) mDumpController.registerDumpable(getDumpableTag(), this);
     }
 
     @Override
@@ -202,10 +202,14 @@
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.removeCallback(this);
         }
-        if (mDumpController != null) mDumpController.removeListener(this);
+        if (mDumpController != null) mDumpController.unregisterDumpable(this);
         super.onDetachedFromWindow();
     }
 
+    protected String getDumpableTag() {
+        return TAG;
+    }
+
     @Override
     public void onTilesChanged() {
         setTiles(mHost.getTiles());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index fed59a5..b27bf32 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -109,7 +109,7 @@
         defaultFactory.setHost(this);
         mQsFactories.add(defaultFactory);
         pluginManager.addPluginListener(this, QSFactory.class, true);
-        mDumpController.addListener(this);
+        mDumpController.registerDumpable(TAG, this);
 
         mainHandler.post(() -> {
             // This is technically a hack to avoid circular dependency of
@@ -131,7 +131,7 @@
         mTunerService.removeTunable(this);
         mServices.destroy();
         mPluginManager.removePluginListener(this);
-        mDumpController.removeListener(this);
+        mDumpController.unregisterDumpable(this);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 73f6fc5..f1e9c87 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -96,6 +96,11 @@
         Dependency.get(TunerService.class).removeTunable(mNumTiles);
     }
 
+    @Override
+    protected String getDumpableTag() {
+        return TAG;
+    }
+
     public void setQSPanelAndHeader(QSPanel fullPanel, View header) {
         mFullPanel = fullPanel;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index a9896f5..822a666 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -22,6 +22,8 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
+import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -48,6 +50,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.text.SimpleDateFormat;
@@ -158,19 +161,34 @@
             case ACTION_STOP:
                 stopRecording();
 
-                // Move temp file to user directory
-                File recordDir = new File(
-                        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
-                        RECORD_DIR);
-                recordDir.mkdirs();
-
                 String fileName = new SimpleDateFormat("'screen-'yyyyMMdd-HHmmss'.mp4'")
                         .format(new Date());
-                Path path = new File(recordDir, fileName).toPath();
 
+                ContentValues values = new ContentValues();
+                values.put(MediaStore.Video.Media.DISPLAY_NAME, fileName);
+                values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
+                values.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis());
+                values.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());
+
+                ContentResolver resolver = getContentResolver();
+                Uri collectionUri = MediaStore.Video.Media.getContentUri(
+                        MediaStore.VOLUME_EXTERNAL_PRIMARY);
+                Uri itemUri = resolver.insert(collectionUri, values);
+
+                File recordDir = new File(getExternalFilesDir(Environment.DIRECTORY_MOVIES),
+                        RECORD_DIR);
+                recordDir.mkdirs();
+                Path path = new File(recordDir, fileName).toPath();
                 try {
+                    // Move file out of temp directory
                     Files.move(mTempFile.toPath(), path);
-                    Notification notification = createSaveNotification(path);
+
+                    // Add to the mediastore
+                    OutputStream os = resolver.openOutputStream(itemUri, "w");
+                    Files.copy(path, os);
+                    os.close();
+
+                    Notification notification = createSaveNotification(itemUri, path);
                     notificationManager.notify(NOTIFICATION_ID, notification);
                 } catch (IOException e) {
                     e.printStackTrace();
@@ -352,13 +370,12 @@
         notificationManager.notify(NOTIFICATION_ID, mRecordingNotificationBuilder.build());
     }
 
-    private Notification createSaveNotification(Path path) {
-        Uri saveUri = FileProvider.getUriForFile(this, FILE_PROVIDER, path.toFile());
-        Log.d(TAG, "Screen recording saved to " + path.toString());
+    private Notification createSaveNotification(Uri uri, Path path) {
+        Log.d(TAG, "Screen recording saved to " + uri.toString() + ", " + path.toString());
 
         Intent viewIntent = new Intent(Intent.ACTION_VIEW)
                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION)
-                .setDataAndType(saveUri, "video/mp4");
+                .setDataAndType(uri, "video/mp4");
 
         Notification.Action shareAction = new Notification.Action.Builder(
                 Icon.createWithResource(this, R.drawable.ic_android),
@@ -366,7 +383,7 @@
                 PendingIntent.getService(
                         this,
                         REQUEST_CODE,
-                        getShareIntent(this, path.toString()),
+                        getShareIntent(this, uri.toString()),
                         PendingIntent.FLAG_UPDATE_CURRENT))
                 .build();
 
@@ -376,7 +393,7 @@
                 PendingIntent.getService(
                         this,
                         REQUEST_CODE,
-                        getDeleteIntent(this, path.toString()),
+                        getDeleteIntent(this, uri.toString()),
                         PendingIntent.FLAG_UPDATE_CURRENT))
                 .build();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 0fe5f8a..4cc5b21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -15,7 +15,6 @@
 package com.android.systemui.statusbar;
 
 import android.content.pm.UserInfo;
-import android.service.notification.StatusBarNotification;
 import android.util.SparseArray;
 
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -58,7 +57,7 @@
 
     boolean shouldHideNotifications(int userId);
     boolean shouldHideNotifications(String key);
-    boolean shouldShowOnKeyguard(StatusBarNotification sbn);
+    boolean shouldShowOnKeyguard(NotificationEntry entry);
 
     boolean isAnyProfilePublicMode();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 4ea1ed5..e08a5ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -33,7 +33,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.service.notification.StatusBarNotification;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -302,7 +301,7 @@
                         Notification.VISIBILITY_SECRET;
     }
 
-    public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
+    public boolean shouldShowOnKeyguard(NotificationEntry entry) {
         if (getEntryManager() == null) {
             Log.wtf(TAG, "mEntryManager was null!", new Throwable());
             return false;
@@ -310,10 +309,10 @@
         boolean exceedsPriorityThreshold;
         if (NotificationUtils.useNewInterruptionModel(mContext)
                 && hideSilentNotificationsOnLockscreen()) {
-            exceedsPriorityThreshold = getEntryManager().getNotificationData().isHighPriority(sbn);
+            exceedsPriorityThreshold = entry.isTopBucket();
         } else {
             exceedsPriorityThreshold =
-                    !getEntryManager().getNotificationData().isAmbient(sbn.getKey());
+                    !getEntryManager().getNotificationData().isAmbient(entry.key);
         }
         return mShowLockscreenNotifications && exceedsPriorityThreshold;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index f001561..00a12a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -17,6 +17,10 @@
 
 import static com.android.systemui.Dependency.MAIN_HANDLER;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_FADING;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController
+        .MODE_WAKE_AND_UNLOCK_PULSING;
 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_ARTWORK;
 import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER;
 import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
@@ -595,9 +599,11 @@
                 boolean cannotAnimateDoze = shadeController != null
                         && shadeController.isDozing()
                         && !ScrimState.AOD.getAnimateChange();
-                if (mBiometricUnlockController != null && mBiometricUnlockController.getMode()
+                boolean needsBypassFading = mKeyguardMonitor.isBypassFadingAnimation();
+                if (((mBiometricUnlockController != null && mBiometricUnlockController.getMode()
                         == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
-                        || hideBecauseOccluded || cannotAnimateDoze) {
+                                || cannotAnimateDoze) && !needsBypassFading)
+                        || hideBecauseOccluded) {
 
                     // We are unlocking directly - no animation!
                     mBackdrop.setVisibility(View.GONE);
@@ -622,9 +628,7 @@
                             });
                     if (mKeyguardMonitor.isKeyguardFadingAway()) {
                         mBackdrop.animate()
-                                // Make it disappear faster, as the focus should be on the activity
-                                // behind.
-                                .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2)
+                                .setDuration(mKeyguardMonitor.getShortenedFadingAwayDuration())
                                 .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay())
                                 .setInterpolator(Interpolators.LINEAR)
                                 .start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 4ccd0cd..99682fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -374,6 +374,10 @@
         clipTransientViews();
 
         setClipTopAmount(clipTopAmount);
+        boolean isHidden = getViewState().hidden || clipTopAmount >= getIntrinsicHeight();
+        if (mShowNotificationShelf) {
+            setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE);
+        }
         setBackgroundTop(backgroundTop);
         setFirstElementRoundness(firstElementRoundness);
         mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 22c9164..6e75c03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -397,15 +397,13 @@
             int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
                     entry.notification) && !entry.isRowRemoved();
-            boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
-                    .notification);
+            boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry);
             if (!showOnKeyguard) {
                 // min priority notifications should show if their summary is showing
                 if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
                     NotificationEntry summary = mGroupManager.getLogicalGroupSummary(
                             entry.notification);
-                    if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
-                            summary.notification))         {
+                    if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(summary)) {
                         showOnKeyguard = true;
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 0d9f4e7..91d4707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -153,6 +153,7 @@
                 if (primary == null) {
                     setAnimationPending(false);
                     invokeCallback(iRemoteAnimationFinishedCallback);
+                    mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
                     return;
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
index ea474ce..314dc04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
@@ -21,6 +21,7 @@
 import android.provider.Settings
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.NotificationMediaManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -40,6 +41,7 @@
         private val bypassController: KeyguardBypassController,
         private val statusBarStateController: StatusBarStateController,
         private val headsUpManager: HeadsUpManagerPhone,
+        private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
         private val mediaManager: NotificationMediaManager,
         tunerService: TunerService) : StatusBarStateController.StateListener,
         NotificationMediaManager.MediaListener {
@@ -63,7 +65,7 @@
                     enabled = Settings.Secure.getIntForUser(
                             context.contentResolver,
                             Settings.Secure.SHOW_MEDIA_WHEN_BYPASSING,
-                            1 /* default */,
+                            0 /* default */,
                             KeyguardUpdateMonitor.getCurrentUser()) != 0
                 }, Settings.Secure.SHOW_MEDIA_WHEN_BYPASSING)
     }
@@ -79,9 +81,6 @@
         if (!NotificationMediaManager.isPlayingState(state)) {
             newEntry = null
         }
-        if (newEntry?.isSensitive == true) {
-            newEntry = null
-        }
         currentMediaEntry = newEntry
         updateAutoHeadsUp(previous)
         updateAutoHeadsUp(currentMediaEntry)
@@ -89,7 +88,7 @@
 
     private fun updateAutoHeadsUp(entry: NotificationEntry?) {
         entry?.let {
-            val autoHeadsUp = it == currentMediaEntry && canAutoHeadsUp()
+            val autoHeadsUp = it == currentMediaEntry && canAutoHeadsUp(it)
             it.isAutoHeadsUp = autoHeadsUp
             if (autoHeadsUp) {
                 headsUpManager.showNotification(it)
@@ -97,11 +96,36 @@
         }
     }
 
+    /**
+     * @return {@code true} if this entry be autoHeadsUpped right now.
+     */
+    private fun canAutoHeadsUp(entry: NotificationEntry): Boolean {
+        if (!isAutoHeadsUpAllowed()) {
+            return false;
+        }
+        if (entry.isSensitive) {
+            // filter sensitive notifications
+            return false
+        }
+        if (!notificationLockscreenUserManager.shouldShowOnKeyguard(entry)) {
+            // filter notifications invisible on Keyguard
+            return false
+        }
+        if (!entryManager.notificationData.activeNotifications.contains(entry)) {
+            // filter notifications not the active list currently
+            return false
+        }
+        return true
+    }
+
     override fun onStatePostChange() {
         updateAutoHeadsUp(currentMediaEntry)
     }
 
-    private fun canAutoHeadsUp() : Boolean {
+    /**
+     * @return {@code true} if autoHeadsUp is possible right now.
+     */
+    private fun isAutoHeadsUpAllowed() : Boolean {
         if (!enabled) {
             return false
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 6a3816c..6af1f5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
+import com.android.systemui.statusbar.phone.PanelExpansionListener
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
 
 import javax.inject.Inject
@@ -40,7 +41,8 @@
         private val mHeadsUpManagerPhone: HeadsUpManagerPhone,
         private val statusBarStateController: StatusBarStateController,
         private val bypassController: KeyguardBypassController)
-    : OnHeadsUpChangedListener, StatusBarStateController.StateListener {
+    : OnHeadsUpChangedListener, StatusBarStateController.StateListener,
+        PanelExpansionListener {
 
     private val mNotificationVisibility
             = object : FloatProperty<NotificationWakeUpCoordinator>("notificationVisibility") {
@@ -64,7 +66,6 @@
     private var mVisibilityAnimator: ObjectAnimator? = null
     private var mVisibilityAmount = 0.0f
     private var mLinearVisibilityAmount = 0.0f
-    private var mWakingUp = false
     private val mEntrySetToClearWhenFinished = mutableSetOf<NotificationEntry>()
     private val mDozeParameters: DozeParameters
     private var pulseExpanding: Boolean = false
@@ -73,6 +74,25 @@
 
     var fullyAwake: Boolean = false
 
+    var wakingUp = false
+        set(value) {
+            field = value
+            willWakeUp = false
+            if (value) {
+                if (mNotificationsVisible && !mNotificationsVisibleForExpansion
+                        && !bypassController.bypassEnabled) {
+                    // We're waking up while pulsing, let's make sure the animation looks nice
+                    mStackScroller.wakeUpFromPulse();
+                }
+                if (bypassController.bypassEnabled && !mNotificationsVisible) {
+                    // Let's make sure our huns become visible once we are waking up in case
+                    // they were blocked by the proximity sensor
+                    updateNotificationVisibility(animate = shouldAnimateVisibility(),
+                            increaseSpeed = false)
+                }
+            }
+        }
+
     var willWakeUp = false
         set(value) {
             if (!value || mDozeAmount != 0.0f) {
@@ -80,7 +100,9 @@
             }
         }
 
+    private var collapsedEnoughToHide: Boolean = false
     lateinit var iconAreaController : NotificationIconAreaController
+
     var pulsing: Boolean = false
         set(value) {
             field = value
@@ -102,7 +124,6 @@
                 }
             }
         }
-
     /**
      * True if we can show pulsing heads up notifications
      */
@@ -112,8 +133,12 @@
             var canShow = pulsing
             if (bypassController.bypassEnabled) {
                 // We also allow pulsing on the lock screen!
-                canShow = canShow || (mWakingUp || willWakeUp || fullyAwake)
+                canShow = canShow || (wakingUp || willWakeUp || fullyAwake)
                         && statusBarStateController.state == StatusBarState.KEYGUARD
+                // We want to hide the notifications when collapsed too much
+                if (collapsedEnoughToHide) {
+                    canShow = false
+                }
             }
             return canShow
         }
@@ -160,7 +185,7 @@
         wakeUpListeners.add(listener);
     }
 
-    fun removeFullyHiddenChangedListener(listener: WakeUpListener) {
+    fun removeListener(listener: WakeUpListener) {
         wakeUpListeners.remove(listener);
     }
 
@@ -169,7 +194,7 @@
         var visible = mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.hasNotifications()
         visible = visible && canShowPulsingHuns
 
-        if (!visible && mNotificationsVisible && (mWakingUp || willWakeUp) && mDozeAmount != 0.0f) {
+        if (!visible && mNotificationsVisible && (wakingUp || willWakeUp) && mDozeAmount != 0.0f) {
             // let's not make notifications invisible while waking up, otherwise the animation
             // is strange
             return;
@@ -229,6 +254,18 @@
         this.state = newState
     }
 
+    override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
+        val collapsedEnough = expansion <= 0.9f
+        if (collapsedEnough != this.collapsedEnoughToHide) {
+            val couldShowPulsingHuns = canShowPulsingHuns;
+            this.collapsedEnoughToHide = collapsedEnough
+            if (couldShowPulsingHuns && !canShowPulsingHuns) {
+                updateNotificationVisibility(animate = true, increaseSpeed = true)
+                mHeadsUpManagerPhone.releaseAllImmediately()
+            }
+        }
+    }
+
     private fun updateDozeAmountIfBypass(): Boolean {
         if (bypassController.bypassEnabled) {
             var amount = 1.0f;
@@ -307,16 +344,6 @@
         return if (bypassController.bypassEnabled) 0.0f else overflow
     }
 
-    fun setWakingUp(wakingUp: Boolean) {
-        willWakeUp = false
-        mWakingUp = wakingUp
-        if (wakingUp && mNotificationsVisible && !mNotificationsVisibleForExpansion
-                && !bypassController.bypassEnabled) {
-            // We're waking up while pulsing, let's make sure the animation looks nice
-            mStackScroller.wakeUpFromPulse();
-        }
-    }
-
     override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
         var animate = shouldAnimateVisibility()
         if (!isHeadsUp) {
@@ -325,7 +352,7 @@
                     // if we animate, we see the shelf briefly visible. Instead we fully animate
                     // the notification and its background out
                     animate = false
-                } else if (!mWakingUp && !willWakeUp){
+                } else if (!wakingUp && !willWakeUp){
                     // TODO: look that this is done properly and not by anyone else
                     entry.setHeadsUpAnimatingAway(true)
                     mEntrySetToClearWhenFinished.add(entry)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
index fca520f..1ce4934 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -25,7 +25,6 @@
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
-import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dependency;
@@ -108,10 +107,19 @@
             boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH
                     && isSystemNotification(nb);
 
-            boolean isHeadsUp = a.getRow().isHeadsUp();
-            if (isHeadsUp != b.getRow().isHeadsUp()) {
-                return isHeadsUp ? -1 : 1;
-            } else if (isHeadsUp) {
+
+            boolean aHeadsUp = a.getRow().isHeadsUp();
+            boolean bHeadsUp = b.getRow().isHeadsUp();
+
+            // HACK: This should really go elsewhere, but it's currently not straightforward to
+            // extract the comparison code and we're guaranteed to touch every element, so this is
+            // the best place to set the buckets for the moment.
+            a.setIsTopBucket(aHeadsUp || aMedia || aSystemMax || a.isHighPriority());
+            b.setIsTopBucket(bHeadsUp || bMedia || bSystemMax || b.isHighPriority());
+
+            if (aHeadsUp != bHeadsUp) {
+                return aHeadsUp ? -1 : 1;
+            } else if (aHeadsUp) {
                 // Provide consistent ranking with headsUpManager
                 return mHeadsUpManager.compare(a, b);
             } else if (aMedia != bMedia) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index b19d2ca..fe88541 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -173,9 +173,13 @@
      * the lock screen/status bar and in the top section in the shade.
      */
     private boolean mHighPriority;
+
+    private boolean mIsTopBucket;
+
     private boolean mSensitive = true;
     private Runnable mOnSensitiveChangedListener;
     private boolean mAutoHeadsUp;
+    private boolean mPulseSupressed;
 
     public NotificationEntry(StatusBarNotification n) {
         this(n, null);
@@ -224,6 +228,18 @@
         this.mHighPriority = highPriority;
     }
 
+    /**
+     * @return True if the notif should appear in the "top" or "important" section of notifications
+     * (as opposed to the "bottom" or "silent" section). This is usually the same as
+     * {@link #isHighPriority()}, but there are certain exceptions, such as media notifs.
+     */
+    public boolean isTopBucket() {
+        return mIsTopBucket;
+    }
+    public void setIsTopBucket(boolean isTopBucket) {
+        mIsTopBucket = isTopBucket;
+    }
+
     public boolean isBubble() {
         return (notification.getNotification().flags & FLAG_BUBBLE) != 0;
     }
@@ -900,6 +916,14 @@
         mOnSensitiveChangedListener = listener;
     }
 
+    public boolean isPulseSuppressed() {
+        return mPulseSupressed;
+    }
+
+    public void setPulseSuppressed(boolean suppressed) {
+        mPulseSupressed = suppressed;
+    }
+
     /** Information about a suggestion that is being edited. */
     public static class EditedSuggestionInfo {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index d057a1d..48a8295 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -349,7 +349,7 @@
         }
 
         if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
-            result.newPublicView = builder.makePublicContentView();
+            result.newPublicView = builder.makePublicContentView(isLowPriority);
         }
 
         result.packageContext = packageContext;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 20e8b73..38f9bff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -39,6 +39,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.widget.MediaNotificationView;
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.TransformableView;
@@ -67,6 +68,7 @@
     private View mSeekBarView;
     private Context mContext;
     private MetricsLogger mMetricsLogger;
+    private boolean mIsViewVisible;
 
     @VisibleForTesting
     protected SeekBar.OnSeekBarChangeListener mSeekListener =
@@ -88,11 +90,33 @@
         }
     };
 
+    MediaNotificationView.VisibilityChangeListener mVisibilityListener =
+            new MediaNotificationView.VisibilityChangeListener() {
+        @Override
+        public void onAggregatedVisibilityChanged(boolean isVisible) {
+            mIsViewVisible = isVisible;
+            if (isVisible) {
+                // Restart timer if we're currently playing and didn't already have one going
+                PlaybackState state = mMediaController.getPlaybackState();
+                if (state != null && state.getState() == PlaybackState.STATE_PLAYING
+                        && mSeekBarTimer == null && mSeekBarView != null
+                        && mSeekBarView.getVisibility() != View.GONE) {
+                    startTimer();
+                }
+            } else {
+                clearTimer();
+            }
+        }
+    };
+
     private MediaController.Callback mMediaCallback = new MediaController.Callback() {
         @Override
         public void onSessionDestroyed() {
             clearTimer();
             mMediaController.unregisterCallback(this);
+            if (mView instanceof MediaNotificationView) {
+                ((MediaNotificationView) mView).removeVisibilityListener(mVisibilityListener);
+            }
         }
 
         @Override
@@ -126,10 +150,16 @@
         mContext = ctx;
         mMediaManager = Dependency.get(NotificationMediaManager.class);
         mMetricsLogger = Dependency.get(MetricsLogger.class);
+
+        if (mView instanceof MediaNotificationView) {
+            MediaNotificationView mediaView = (MediaNotificationView) mView;
+            mediaView.addVisibilityListener(mVisibilityListener);
+        }
     }
 
     private void resolveViews() {
         mActions = mView.findViewById(com.android.internal.R.id.media_actions);
+        mIsViewVisible = mView.isShown();
 
         final MediaSession.Token token = mRow.getEntry().notification.getNotification().extras
                 .getParcelable(Notification.EXTRA_MEDIA_SESSION);
@@ -208,18 +238,19 @@
 
     private void startTimer() {
         clearTimer();
-        mSeekBarTimer = new Timer(true /* isDaemon */);
-        mSeekBarTimer.schedule(new TimerTask() {
-            @Override
-            public void run() {
-                mHandler.post(mOnUpdateTimerTick);
-            }
-        }, 0, PROGRESS_UPDATE_INTERVAL);
+        if (mIsViewVisible) {
+            mSeekBarTimer = new Timer(true /* isDaemon */);
+            mSeekBarTimer.schedule(new TimerTask() {
+                @Override
+                public void run() {
+                    mHandler.post(mOnUpdateTimerTick);
+                }
+            }, 0, PROGRESS_UPDATE_INTERVAL);
+        }
     }
 
     private void clearTimer() {
         if (mSeekBarTimer != null) {
-            // TODO: also trigger this when the notification panel is collapsed
             mSeekBarTimer.cancel();
             mSeekBarTimer.purge();
             mSeekBarTimer = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 212808d..15cc72c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -184,4 +184,6 @@
     default boolean containsView(View v) {
         return true;
     }
+
+    default void setWillExpand(boolean willExpand) {};
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index 170a4d5..d119fb79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -133,7 +133,7 @@
             if (child instanceof ExpandableNotificationRow
                     && child.getVisibility() != View.GONE) {
                 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                if (!row.getEntry().isHighPriority()) {
+                if (!row.getEntry().isTopBucket()) {
                     firstGentleNotifIndex = i;
                     mFirstGentleNotif = row;
                     break;
@@ -248,7 +248,7 @@
             View child = mParent.getChildAt(i);
             if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) {
                 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                if (!row.getEntry().isHighPriority()) {
+                if (!row.getEntry().isTopBucket()) {
                     break;
                 } else {
                     lastChildBeforeGap = row;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 04cbb13..1a41866 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -411,6 +411,7 @@
                 outline.setRoundRect(mBackgroundAnimationRect,
                         MathUtils.lerp(mCornerRadius / 2.0f, mCornerRadius,
                                 xProgress));
+                outline.setAlpha(1.0f - mAmbientState.getHideAmount());
             } else {
                 ViewOutlineProvider.BACKGROUND.getOutline(view, outline);
             }
@@ -499,6 +500,7 @@
     private boolean mAnimateBottomOnLayout;
     private float mLastSentAppear;
     private float mLastSentExpandedHeight;
+    private boolean mWillExpand;
 
     @Inject
     public NotificationStackScrollLayout(
@@ -1043,6 +1045,7 @@
         requestChildrenUpdate();
         updateFirstAndLastBackgroundViews();
         updateAlgorithmLayoutMinHeight();
+        updateOwnTranslationZ();
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4406,6 +4409,7 @@
         mStateAnimator.setShadeExpanded(isExpanded);
         mSwipeHelper.setIsExpanded(isExpanded);
         if (changed) {
+            mWillExpand = false;
             if (!mIsExpanded) {
                 mGroupManager.collapseAllGroups();
                 mExpandHelper.cancelImmediately();
@@ -4774,6 +4778,20 @@
         updateAlgorithmHeightAndPadding();
         updateBackgroundDimming();
         requestChildrenUpdate();
+        updateOwnTranslationZ();
+    }
+
+    private void updateOwnTranslationZ() {
+        // Since we are clipping to the outline we need to make sure that the shadows aren't
+        // clipped when pulsing
+        float ownTranslationZ = 0;
+        if (mKeyguardBypassController.getBypassEnabled() && mAmbientState.isHiddenAtAll()) {
+            ExpandableView firstChildNotGone = getFirstChildNotGone();
+            if (firstChildNotGone != null && firstChildNotGone.showingPulsing()) {
+                ownTranslationZ = firstChildNotGone.getTranslationZ();
+            }
+        }
+        setTranslationZ(ownTranslationZ);
     }
 
     private void updateVisibility() {
@@ -5054,7 +5072,7 @@
         if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
             mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
             mNeedsAnimation = true;
-            if (!mIsExpanded && !isHeadsUp) {
+            if (!mIsExpanded && !mWillExpand && !isHeadsUp) {
                 row.setHeadsUpAnimatingAway(true);
             }
             requestChildrenUpdate();
@@ -5075,6 +5093,11 @@
         requestChildrenUpdate();
     }
 
+    @Override
+    public void setWillExpand(boolean willExpand) {
+        mWillExpand = willExpand;
+    }
+
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setTrackingHeadsUp(ExpandableNotificationRow row) {
         mTrackingHeadsUp = row != null;
@@ -5675,6 +5698,8 @@
             // The bottom might change because we're using the final actual height of the view
             mAnimateBottomOnLayout = true;
         }
+        // Let's update the footer once the notifications have been updated (in the next frame)
+        post(this::updateFooter);
     }
 
     public void setOnPulseHeightChangedListener(Runnable listener) {
@@ -5745,7 +5770,7 @@
             currentIndex++;
             boolean beforeSpeedBump;
             if (mHighPriorityBeforeSpeedBump) {
-                beforeSpeedBump = row.getEntry().isHighPriority();
+                beforeSpeedBump = row.getEntry().isTopBucket();
             } else {
                 beforeSpeedBump = !row.getEntry().ambient;
             }
@@ -5803,9 +5828,9 @@
             case ROWS_ALL:
                 return true;
             case ROWS_HIGH_PRIORITY:
-                return row.getEntry().isHighPriority();
+                return row.getEntry().isTopBucket();
             case ROWS_GENTLE:
-                return !row.getEntry().isHighPriority();
+                return !row.getEntry().isTopBucket();
             default:
                 throw new IllegalArgumentException("Unknown selection: " + selection);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 94cd2cd..41c6a7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -445,14 +445,13 @@
         if (!mUpdateMonitor.isDeviceInteractive()) {
             if (!mStatusBarKeyguardViewManager.isShowing()) {
                 return bypass ? MODE_WAKE_AND_UNLOCK : MODE_ONLY_WAKE;
-            } else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
+            } else if (!unlockingAllowed) {
+                return bypass ? MODE_SHOW_BOUNCER : MODE_NONE;
+            } else if (mDozeScrimController.isPulsing()) {
                 // Let's not wake-up to lock screen when not bypassing, otherwise the notification
                 // would move as the user tried to tap it.
                 return bypass ? MODE_WAKE_AND_UNLOCK_PULSING : MODE_NONE;
             } else {
-                if (!(mDozeScrimController.isPulsing() && !unlockingAllowed)) {
-                    Log.wtf(TAG, "Face somehow arrived when the device was not interactive");
-                }
                 if (bypass) {
                     // Wake-up fading out nicely
                     return MODE_WAKE_AND_UNLOCK_PULSING;
@@ -530,7 +529,8 @@
         mStatusBar.notifyBiometricAuthModeChanged();
     }
 
-    private final WakefulnessLifecycle.Observer mWakefulnessObserver =
+    @VisibleForTesting
+    final WakefulnessLifecycle.Observer mWakefulnessObserver =
             new WakefulnessLifecycle.Observer() {
         @Override
         public void onFinishedWakingUp() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 539bc7b..d22ad71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -16,6 +16,7 @@
 
 import static com.android.systemui.Interpolators.ALPHA_IN;
 import static com.android.systemui.Interpolators.ALPHA_OUT;
+import static com.android.systemui.Interpolators.LINEAR;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -33,7 +34,7 @@
  */
 public class ButtonDispatcher {
     private final static int FADE_DURATION_IN = 150;
-    private final static int FADE_DURATION_OUT = 100;
+    private final static int FADE_DURATION_OUT = 1000;
 
     private final ArrayList<View> mViews = new ArrayList<>();
 
@@ -178,7 +179,7 @@
             setVisibility(View.VISIBLE);
             mFadeAnimator = ValueAnimator.ofFloat(getAlpha(), alpha);
             mFadeAnimator.setDuration(duration);
-            mFadeAnimator.setInterpolator(getAlpha() < alpha ? ALPHA_IN : ALPHA_OUT);
+            mFadeAnimator.setInterpolator(LINEAR);
             mFadeAnimator.addListener(mFadeListener);
             mFadeAnimator.addUpdateListener(mAlphaListener);
             mFadeAnimator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 46dd5e6..f53c4e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -35,6 +35,7 @@
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -48,7 +49,7 @@
  * Controls the appearance of heads up notifications in the icon area and the header itself.
  */
 public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
-        DarkIconDispatcher.DarkReceiver {
+        DarkIconDispatcher.DarkReceiver, NotificationWakeUpCoordinator.WakeUpListener {
     public static final int CONTENT_FADE_DURATION = 110;
     public static final int CONTENT_FADE_DELAY = 100;
     private final NotificationIconAreaController mNotificationIconAreaController;
@@ -67,6 +68,7 @@
     private final KeyguardBypassController mBypassController;
     private final StatusBarStateController mStatusBarStateController;
     private final CommandQueue mCommandQueue;
+    private final NotificationWakeUpCoordinator mWakeUpCoordinator;
     @VisibleForTesting
     float mExpandedHeight;
     @VisibleForTesting
@@ -95,9 +97,10 @@
             HeadsUpManagerPhone headsUpManager,
             View statusbarView,
             SysuiStatusBarStateController statusBarStateController,
-            KeyguardBypassController keyguardBypassController) {
+            KeyguardBypassController keyguardBypassController,
+            NotificationWakeUpCoordinator wakeUpCoordinator) {
         this(notificationIconAreaController, headsUpManager, statusBarStateController,
-                keyguardBypassController,
+                keyguardBypassController, wakeUpCoordinator,
                 statusbarView.findViewById(R.id.heads_up_status_bar_view),
                 statusbarView.findViewById(R.id.notification_stack_scroller),
                 statusbarView.findViewById(R.id.notification_panel),
@@ -112,6 +115,7 @@
             HeadsUpManagerPhone headsUpManager,
             StatusBarStateController stateController,
             KeyguardBypassController bypassController,
+            NotificationWakeUpCoordinator wakeUpCoordinator,
             HeadsUpStatusBarView headsUpStatusBarView,
             NotificationStackScrollLayout stackScroller,
             NotificationPanelView panelView,
@@ -153,6 +157,8 @@
         });
         mBypassController = bypassController;
         mStatusBarStateController = stateController;
+        mWakeUpCoordinator = wakeUpCoordinator;
+        wakeUpCoordinator.addListener(this);
         mCommandQueue = getComponent(headsUpStatusBarView.getContext(), CommandQueue.class);
         mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
     }
@@ -161,6 +167,7 @@
     public void destroy() {
         mHeadsUpManager.removeListener(this);
         mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null);
+        mWakeUpCoordinator.removeListener(this);
         mPanelView.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
         mPanelView.removeVerticalTranslationListener(mUpdatePanelTranslation);
         mPanelView.setHeadsUpAppearanceController(null);
@@ -289,6 +296,11 @@
                     updateParentClipping(true /* shouldClip */);
                 });
             }
+            // Show the status bar icons when the view gets shown / hidden
+            if (mStatusBarStateController.getState() != StatusBarState.SHADE) {
+                mCommandQueue.recomputeDisableFlags(
+                        mHeadsUpStatusBarView.getContext().getDisplayId(), false);
+            }
         }
     }
 
@@ -362,10 +374,12 @@
      * @return if the heads up status bar view should be shown
      */
     public boolean shouldBeVisible() {
-        boolean canShow = !mIsExpanded;
+        boolean notificationsShown = !mWakeUpCoordinator.getNotificationsFullyHidden();
+        boolean canShow = !mIsExpanded && notificationsShown;
         if (mBypassController.getBypassEnabled() &&
                 (mStatusBarStateController.getState() == StatusBarState.KEYGUARD
-                        || mKeyguardMonitor.isKeyguardGoingAway())) {
+                        || mKeyguardMonitor.isKeyguardGoingAway())
+                && notificationsShown) {
             canShow = true;
         }
         return canShow && mHeadsUpManager.hasPinnedHeadsUp();
@@ -377,15 +391,6 @@
         updateHeader(entry);
     }
 
-    @Override
-    public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
-        if (mStatusBarStateController.getState() != StatusBarState.SHADE) {
-            // Show the status bar icons when the pinned mode changes
-            mCommandQueue.recomputeDisableFlags(
-                    mHeadsUpStatusBarView.getContext().getDisplayId(), false);
-        }
-    }
-
     public void setAppearFraction(float expandedHeight, float appearFraction) {
         boolean changed = expandedHeight != mExpandedHeight;
         mExpandedHeight = expandedHeight;
@@ -451,4 +456,9 @@
             mAppearFraction = oldController.mAppearFraction;
         }
     }
+
+    @Override
+    public void onFullyHiddenChanged(boolean isFullyHidden) {
+        updateTopEntry();
+    }
 }
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 d6f8a60..c4d346c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -170,7 +170,7 @@
 
         // Split up the work over multiple frames.
         DejankUtils.removeCallbacks(mResetRunnable);
-        if (mUnlockMethodCache.isUnlockingWithFacePossible() && !needsFullscreenBouncer()
+        if (mUnlockMethodCache.isFaceAuthEnabled() && !needsFullscreenBouncer()
                 && !mKeyguardUpdateMonitor.userNeedsStrongAuth()) {
             mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
         } else {
@@ -207,14 +207,12 @@
      * @see #onFullyShown()
      */
     private void onFullyHidden() {
-        if (!mShowingSoon) {
-            cancelShowRunnable();
-            if (mRoot != null) {
-                mRoot.setVisibility(View.INVISIBLE);
-            }
-            mFalsingManager.onBouncerHidden();
-            DejankUtils.postAfterTraversal(mResetRunnable);
+        cancelShowRunnable();
+        if (mRoot != null) {
+            mRoot.setVisibility(View.INVISIBLE);
         }
+        mFalsingManager.onBouncerHidden();
+        DejankUtils.postAfterTraversal(mResetRunnable);
     }
 
     private final Runnable mShowRunnable = new Runnable() {
@@ -349,7 +347,7 @@
      * {@link #show(boolean)} was called but we're not showing yet, or being dragged.
      */
     public boolean inTransit() {
-        return mShowingSoon || mExpansion != EXPANSION_HIDDEN;
+        return mShowingSoon || mExpansion != EXPANSION_HIDDEN && mExpansion != EXPANSION_VISIBLE;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 0aec2b1..70d3bff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -47,7 +47,7 @@
      * If face unlock dismisses the lock screen or keeps user on keyguard for the current user.
      */
     var bypassEnabled: Boolean = false
-        get() = field && unlockMethodCache.isUnlockingWithFacePossible
+        get() = field && unlockMethodCache.isFaceAuthEnabled
         private set
 
     var bouncerShowing: Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 1360a08..06a2225 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -217,7 +217,7 @@
         mConfigurationController.removeCallback(this);
         mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
         mKeyguardMonitor.removeCallback(mKeyguardMonitorCallback);
-        mWakeUpCoordinator.removeFullyHiddenChangedListener(this);
+        mWakeUpCoordinator.removeListener(this);
         mUnlockMethodCache.removeListener(this);
         if (mDockManager != null) {
             mDockManager.removeListener(mDockEventListener);
@@ -323,14 +323,24 @@
         }
         updateDarkTint();
 
+        updateIconVisibility();
+        updateClickability();
+
+        return true;
+    }
+
+    /**
+     * Update the icon visibility
+     * @return true if the visibility changed
+     */
+    private boolean updateIconVisibility() {
         boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked);
         boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning
                 || mShowingLaunchAffordance;
         if (mBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) {
-            if (mHeadsUpManager.isHeadsUpGoingAway()
-                    || mHeadsUpManager.hasPinnedHeadsUp()
-                    || (mStatusBarStateController.getState() == StatusBarState.KEYGUARD
-                    && !mWakeUpCoordinator.getNotificationsFullyHidden())) {
+            if ((mHeadsUpManager.isHeadsUpGoingAway() || mHeadsUpManager.hasPinnedHeadsUp()
+                    || mStatusBarStateController.getState() == StatusBarState.KEYGUARD)
+                    && !mWakeUpCoordinator.getNotificationsFullyHidden()) {
                 invisible = true;
             }
         }
@@ -349,10 +359,9 @@
                         .setDuration(233)
                         .start();
             }
+            return true;
         }
-        updateClickability();
-
-        return true;
+        return false;
     }
 
     private boolean canBlockUpdates() {
@@ -440,7 +449,10 @@
     @Override
     public void onFullyHiddenChanged(boolean isFullyHidden) {
         if (mBypassController.getBypassEnabled()) {
-            update();
+            boolean changed = updateIconVisibility();
+            if (changed) {
+                update();
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 22e3edb..081e293 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -21,7 +21,9 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SEARCH_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
 
@@ -706,7 +708,7 @@
         }
     }
 
-    public void onPanelExpandedChange() {
+    public void onStatusBarPanelStateChanged() {
         updateSlippery();
         updateSystemUiStateFlags();
     }
@@ -719,9 +721,13 @@
                 (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0, displayId);
         mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_HOME_DISABLED,
                 (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0, displayId);
+        mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SEARCH_DISABLED,
+                (mDisabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0, displayId);
         if (mPanelView != null) {
             mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
                     mPanelView.isFullyExpanded() && !mPanelView.isInSettings(), displayId);
+            mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
+                    mPanelView.isInSettings(), displayId);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
index 0fe1294..4f7af580 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
@@ -53,8 +53,8 @@
         final int dualToneLightTheme = Utils.getThemeAttr(context, R.attr.lightIconTheme);
         Context lightContext = new ContextThemeWrapper(context, dualToneLightTheme);
         Context darkContext = new ContextThemeWrapper(context, dualToneDarkTheme);
-        mLightColor = Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor);
-        mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor);
+        mLightColor = Utils.getColorAttrDefaultColor(lightContext, R.attr.homeHandleColor);
+        mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.homeHandleColor);
         mPaint.setAntiAlias(true);
         setFocusable(false);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 21de8a5..ba34069 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -78,6 +78,7 @@
     private int mAodIconTint;
     private boolean mFullyHidden;
     private boolean mAodIconsVisible;
+    private boolean mIsPulsing;
 
     public NotificationIconAreaController(Context context, StatusBar statusBar,
             StatusBarStateController statusBarStateController,
@@ -265,7 +266,9 @@
         if (!showAmbient && entry.shouldSuppressStatusBar()) {
             return false;
         }
-        if (hidePulsing && entry.showingPulsing()) {
+        if (hidePulsing && entry.showingPulsing()
+                && (!mWakeUpCoordinator.getNotificationsFullyHidden()
+                        || !entry.isPulseSuppressed())) {
             return false;
         }
         return true;
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 971a7ee..2bfd6ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -40,6 +40,7 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.PowerManager;
+import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.MathUtils;
@@ -139,6 +140,12 @@
     private static final int CAP_HEIGHT = 1456;
     private static final int FONT_HEIGHT = 2163;
 
+    /**
+     * Maximum time before which we will expand the panel even for slow motions when getting a
+     * touch passed over from launcher.
+     */
+    private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300;
+
     static final String COUNTER_PANEL_OPEN = "panel_open";
     static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
     private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
@@ -374,6 +381,8 @@
     private boolean mHeadsUpPinnedMode;
     private float mKeyguardHeadsUpShowingAmount = 0.0f;
     private boolean mShowingKeyguardHeadsUp;
+    private boolean mAllowExpandForSmallExpansion;
+    private Runnable mExpandAfterLayoutRunnable;
 
     @Inject
     public NotificationPanelView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
@@ -454,6 +463,11 @@
         mPulseExpansionHandler.setUp(mNotificationStackScroller, this, mShadeController);
         mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
             @Override
+            public void onFullyHiddenChanged(boolean isFullyHidden) {
+                updateKeyguardStatusBarForHeadsUp();
+            }
+
+            @Override
             public void onPulseExpansionChanged(boolean expandingChanged) {
                 if (mKeyguardBypassController.getBypassEnabled()) {
                     // Position the notifications while dragging down while pulsing
@@ -666,6 +680,10 @@
         }
         updateMaxHeadsUpTranslation();
         updateGestureExclusionRect();
+        if (mExpandAfterLayoutRunnable != null) {
+            mExpandAfterLayoutRunnable.run();
+            mExpandAfterLayoutRunnable = null;
+        }
     }
 
     private void updateGestureExclusionRect() {
@@ -803,8 +821,7 @@
             if (suppressedSummary) {
                 continue;
             }
-            if (!mLockscreenUserManager.shouldShowOnKeyguard(
-                    row.getStatusBarNotification())) {
+            if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) {
                 continue;
             }
             if (row.isRemoved()) {
@@ -1065,6 +1082,8 @@
             mDownY = event.getY();
             mCollapsedOnDown = isFullyCollapsed();
             mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
+            mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
+            mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
             if (mExpectingSynthesizedDown) {
                 mLastEventSynthesizedDown = true;
             } else {
@@ -1123,6 +1142,20 @@
     }
 
     @Override
+    protected boolean shouldExpandWhenNotFlinging() {
+        if (super.shouldExpandWhenNotFlinging()) {
+            return true;
+        }
+        if (mAllowExpandForSmallExpansion) {
+            // When we get a touch that came over from launcher, the velocity isn't always correct
+            // Let's err on expanding if the gesture has been reasonably slow
+            long timeSinceDown = SystemClock.uptimeMillis() - mDownTime;
+            return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER;
+        }
+        return false;
+    }
+
+    @Override
     protected float getOpeningHeight() {
         return mNotificationStackScroller.getOpeningHeight();
     }
@@ -1278,6 +1311,7 @@
         }
         mExpectingSynthesizedDown = true;
         onTrackingStarted();
+        updatePanelExpanded();
     }
 
     /**
@@ -1294,10 +1328,19 @@
      *
      * @param velocity unit is in px / millis
      */
-    public void stopWaitingForOpenPanelGesture(float velocity) {
+    public void stopWaitingForOpenPanelGesture(final float velocity) {
         if (mExpectingSynthesizedDown) {
             mExpectingSynthesizedDown = false;
-            fling(velocity > 1f ? 1000f * velocity : 0, true /* animate */);
+            maybeVibrateOnOpening();
+            Runnable runnable = () -> fling(velocity > 1f ? 1000f * velocity : 0,
+                    true /* expand */);
+            if (mStatusBar.getStatusBarWindow().getHeight()
+                    != mStatusBar.getStatusBarHeight()) {
+                // The panel is already expanded to its full size, let's expand directly
+                runnable.run();
+            } else {
+                mExpandAfterLayoutRunnable = runnable;
+            }
             onTrackingStopped(false);
         }
     }
@@ -1560,9 +1603,15 @@
         anim.setStartDelay(mKeyguardMonitor.isKeyguardFadingAway()
                 ? mKeyguardMonitor.getKeyguardFadingAwayDelay()
                 : 0);
-        anim.setDuration(mKeyguardMonitor.isKeyguardFadingAway()
-                ? mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2
-                : StackStateAnimator.ANIMATION_DURATION_STANDARD);
+
+        long duration;
+        if (mKeyguardMonitor.isKeyguardFadingAway()) {
+            duration = mKeyguardMonitor.getShortenedFadingAwayDuration();
+        } else {
+            duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
+        }
+        anim.setDuration(duration);
+
         anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -1605,7 +1654,7 @@
             mKeyguardBottomArea.animate()
                     .alpha(0f)
                     .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay())
-                    .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2)
+                    .setDuration(mKeyguardMonitor.getShortenedFadingAwayDuration())
                     .setInterpolator(Interpolators.ALPHA_OUT)
                     .withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable)
                     .start();
@@ -1634,7 +1683,7 @@
             if (keyguardFadingAway) {
                 mKeyguardStatusView.animate()
                         .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay())
-                        .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2)
+                        .setDuration(mKeyguardMonitor.getShortenedFadingAwayDuration())
                         .start();
             }
         } else if (mBarState == StatusBarState.SHADE_LOCKED
@@ -1717,8 +1766,8 @@
             mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
                     false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
         }
-        if (mExpansionListener != null) {
-            mExpansionListener.onQsExpansionChanged(mQsMaxExpansionHeight != 0
+        for (int i = 0; i < mExpansionListeners.size(); i++) {
+            mExpansionListeners.get(i).onQsExpansionChanged(mQsMaxExpansionHeight != 0
                     ? mQsExpansionHeight / mQsMaxExpansionHeight : 0);
         }
         if (DEBUG) {
@@ -2029,7 +2078,7 @@
     }
 
     private void updatePanelExpanded() {
-        boolean isExpanded = !isFullyCollapsed();
+        boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
         if (mPanelExpanded != isExpanded) {
             mHeadsUpManager.setIsPanelExpanded(isExpanded);
             mStatusBar.setPanelExpanded(isExpanded);
@@ -3374,24 +3423,4 @@
         mOnReinflationListener = onReinflationListener;
     }
 
-    /**
-     * Panel and QS expansion callbacks.
-     */
-    public interface PanelExpansionListener {
-        /**
-         * Invoked whenever the notification panel expansion changes, at every animation frame.
-         * This is the main expansion that happens when the user is swiping up to dismiss the
-         * lock screen.
-         *
-         * @param expansion 0 when collapsed, 1 when expanded.
-         * @param tracking {@code true} when the user is actively dragging the panel.
-         */
-        void onPanelExpansionChanged(float expansion, boolean tracking);
-
-        /**
-         * Invoked whenever the QS expansion changes, at every animation frame.
-         * @param expansion 0 when collapsed, 1 when expanded.
-         */
-        void onQsExpansionChanged(float expansion);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelExpansionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelExpansionListener.java
new file mode 100644
index 0000000..655a25d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelExpansionListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+/**
+ * Panel and QS expansion callbacks.
+ */
+public interface PanelExpansionListener {
+    /**
+     * Invoked whenever the notification panel expansion changes, at every animation frame.
+     * This is the main expansion that happens when the user is swiping up to dismiss the
+     * lock screen.
+     *
+     * @param expansion 0 when collapsed, 1 when expanded.
+     * @param tracking {@code true} when the user is actively dragging the panel.
+     */
+    void onPanelExpansionChanged(float expansion, boolean tracking);
+
+    /**
+     * Invoked whenever the QS expansion changes, at every animation frame.
+     * @param expansion 0 when collapsed, 1 when expanded.
+     */
+    default void onQsExpansionChanged(float expansion) {};
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 853faab..31600e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -49,11 +49,11 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.phone.NotificationPanelView.PanelExpansionListener;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 
 public abstract class PanelView extends FrameLayout {
     public static final boolean DEBUG = PanelBar.DEBUG;
@@ -61,14 +61,15 @@
     private static final int INITIAL_OPENING_PEEK_DURATION = 200;
     private static final int PEEK_ANIMATION_DURATION = 360;
     private static final int NO_FIXED_DURATION = -1;
-    private long mDownTime;
+    protected long mDownTime;
+    protected boolean mTouchSlopExceededBeforeDown;
     private float mMinExpandHeight;
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
     private boolean mPanelUpdateWhenAnimatorEnds;
     private boolean mVibrateOnOpening;
     protected boolean mLaunchingNotification;
     private int mFixedDuration = NO_FIXED_DURATION;
-    protected PanelExpansionListener mExpansionListener;
+    protected ArrayList<PanelExpansionListener> mExpansionListeners = new ArrayList<>();
 
     private final void logf(String fmt, Object... args) {
         Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
@@ -322,7 +323,7 @@
                 if (!mGestureWaitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning)
                         || mPeekAnimator != null) {
                     mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning)
-                            || mPeekAnimator != null;
+                            || mPeekAnimator != null || mTouchSlopExceededBeforeDown;
                     cancelHeightAnimator();
                     cancelPeek();
                     onTrackingStarted();
@@ -408,9 +409,7 @@
         runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(),
                 false /* collapseWhenFinished */);
         notifyBarPanelExpansionChanged();
-        if (mVibrateOnOpening) {
-            mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
-        }
+        maybeVibrateOnOpening();
 
         //TODO: keyguard opens QS a different way; log that too?
 
@@ -425,6 +424,12 @@
                 rot);
     }
 
+    protected void maybeVibrateOnOpening() {
+        if (mVibrateOnOpening) {
+            mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+        }
+    }
+
     protected abstract float getOpeningHeight();
 
     /**
@@ -576,7 +581,7 @@
                 mInitialTouchY = y;
                 mInitialTouchX = x;
                 mTouchStartedInEmptyArea = !isInContentBounds(x, y);
-                mTouchSlopExceeded = false;
+                mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
                 mJustPeeked = false;
                 mMotionAborted = false;
                 mPanelClosedOnDown = isFullyCollapsed();
@@ -679,12 +684,16 @@
             return true;
         }
         if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
-            return getExpandedFraction() > 0.5f;
+            return shouldExpandWhenNotFlinging();
         } else {
             return vel > 0;
         }
     }
 
+    protected boolean shouldExpandWhenNotFlinging() {
+        return getExpandedFraction() > 0.5f;
+    }
+
     /**
      * @param x the final x-coordinate when the finger was lifted
      * @param y the final y-coordinate when the finger was lifted
@@ -1173,13 +1182,13 @@
                     || mPeekAnimator != null || mInstantExpanding
                     || isPanelVisibleBecauseOfHeadsUp() || mTracking || mHeightAnimator != null);
         }
-        if (mExpansionListener != null) {
-            mExpansionListener.onPanelExpansionChanged(mExpandedFraction, mTracking);
+        for (int i = 0; i < mExpansionListeners.size(); i++) {
+            mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking);
         }
     }
 
-    public void setExpansionListener(PanelExpansionListener panelExpansionListener) {
-        mExpansionListener = panelExpansionListener;
+    public void addExpansionListener(PanelExpansionListener panelExpansionListener) {
+        mExpansionListeners.add(panelExpansionListener);
     }
 
     protected abstract boolean isPanelVisibleBecauseOfHeadsUp();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 660810f..8efd952 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -46,7 +46,6 @@
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.Objects;
 
@@ -279,7 +278,7 @@
         super.panelExpansionChanged(frac, expanded);
         updateScrimFraction();
         if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
-            mBar.getNavigationBarView().onPanelExpandedChange();
+            mBar.getNavigationBarView().onStatusBarPanelStateChanged();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 1aec5e4..b12bf5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -491,8 +491,10 @@
      * away once the display turns on.
      */
     public void prepareForGentleWakeUp() {
-        if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()) {
+        if (mState == ScrimState.AOD) {
             mCurrentInFrontAlpha = 1f;
+            mCurrentInFrontTint = Color.BLACK;
+            mCurrentBehindTint = Color.BLACK;
             mAnimateChange = false;
             updateScrims();
             mAnimateChange = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 763e0d7..c706062 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -129,7 +129,10 @@
         public void prepare(ScrimState previousState) {
             mCurrentInFrontAlpha = 0f;
             mCurrentBehindTint = Color.BLACK;
+            mCurrentInFrontTint = Color.BLACK;
             mBlankScreen = mDisplayRequiresBlanking;
+            mAnimationDuration = mWakeLockScreenSensorActive
+                    ? ScrimController.ANIMATION_DURATION_LONG : ScrimController.ANIMATION_DURATION;
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index f8e6aa3..f15b601 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -489,9 +489,12 @@
             WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
             final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
                     com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
+            final boolean imageWallpaperInAmbient =
+                    !DozeParameters.getInstance(mContext).getDisplayNeedsBlanking();
             // If WallpaperInfo is null, it must be ImageWallpaper.
             final boolean supportsAmbientMode = deviceSupportsAodWallpaper
-                    && (info == null || info.supportsAmbientMode());
+                    && ((info == null && imageWallpaperInAmbient)
+                        || (info != null && info.supportsAmbientMode()));
 
             mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
             mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
@@ -813,6 +816,7 @@
         inflateShelf();
         mNotificationIconAreaController.setupShelf(mNotificationShelf);
         mNotificationPanel.setOnReinflationListener(mNotificationIconAreaController::initAodIcons);
+        mNotificationPanel.addExpansionListener(mWakeUpCoordinator);
 
         Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
         // Allow plugins to reference DarkIconDispatcher and StatusBarStateController
@@ -855,7 +859,8 @@
                     }
                     mHeadsUpAppearanceController = new HeadsUpAppearanceController(
                             mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow,
-                            mStatusBarStateController, mKeyguardBypassController);
+                            mStatusBarStateController, mKeyguardBypassController,
+                            mWakeUpCoordinator);
                     mHeadsUpAppearanceController.readFrom(oldController);
                     mStatusBarWindow.setStatusBarView(mStatusBarView);
                     updateAreThereNotifications();
@@ -1507,6 +1512,9 @@
         mNotificationPanel.setStatusAccessibilityImportance(expanded
                 ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
                 : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+        if (getNavigationBarView() != null) {
+            getNavigationBarView().onStatusBarPanelStateChanged();
+        }
     }
 
     public boolean isWakeUpComingFromTouch() {
@@ -1577,7 +1585,8 @@
     public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
         mEntryManager.updateNotifications();
         if (isDozing() && isHeadsUp) {
-            mDozeServiceHost.fireNotificationPulse();
+            entry.setPulseSuppressed(false);
+            mDozeServiceHost.fireNotificationPulse(entry);
             if (mPulsing) {
                 mDozeScrimController.cancelPendingPulseTimeout();
             }
@@ -1801,6 +1810,8 @@
                     mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
                 }
                 mNotificationPanel.expand(true /* animate */);
+                ((NotificationListContainer) mStackScroller).setWillExpand(true);
+                mHeadsUpManager.unpinAll(true /* userUnpinned */);
                 mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1);
             } else if (!mNotificationPanel.isInSettings() && !mNotificationPanel.isExpanding()){
                 mNotificationPanel.flingSettings(0 /* velocity */,
@@ -1938,7 +1949,6 @@
 
         if (start) {
             mNotificationPanel.startWaitingForOpenPanelGesture();
-            setPanelExpanded(true);
         } else {
             mNotificationPanel.stopWaitingForOpenPanelGesture(velocity);
         }
@@ -2387,6 +2397,10 @@
             mLightBarController.dump(fd, pw, args);
         }
 
+        if (mUnlockMethodCache != null) {
+            mUnlockMethodCache.dump(pw);
+        }
+
         if (mKeyguardBypassController != null) {
             mKeyguardBypassController.dump(pw);
         }
@@ -3199,12 +3213,13 @@
 
     /**
      * Notifies the status bar the Keyguard is fading away with the specified timings.
-     *
-     * @param startTime the start time of the animations in uptime millis
+     *  @param startTime the start time of the animations in uptime millis
      * @param delay the precalculated animation delay in milliseconds
      * @param fadeoutDuration the duration of the exit animation, in milliseconds
+     * @param isBypassFading is this a fading away animation while bypassing
      */
-    public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration) {
+    public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration,
+            boolean isBypassFading) {
         mCommandQueue.appTransitionStarting(mDisplayId, startTime + fadeoutDuration
                         - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
                 LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
@@ -3212,7 +3227,7 @@
         mCommandQueue.appTransitionStarting(mDisplayId,
                     startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
                     LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
-        mKeyguardMonitor.notifyKeyguardFadingAway(delay, fadeoutDuration);
+        mKeyguardMonitor.notifyKeyguardFadingAway(delay, fadeoutDuration, isBypassFading);
     }
 
     /**
@@ -3562,6 +3577,9 @@
                 userAllowsPrivateNotificationsInPublic(mLockscreenUserManager.getCurrentUserId())
                 || !mLockscreenUserManager.shouldShowLockscreenNotifications()
                 || mFalsingManager.shouldEnforceBouncer();
+        if (mKeyguardBypassController.getBypassEnabled()) {
+            fullShadeNeedsBouncer = false;
+        }
         if (mLockscreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
             mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
             showBouncerIfKeyguard();
@@ -3919,9 +3937,13 @@
             }
         }
 
-        public void fireNotificationPulse() {
+        public void fireNotificationPulse(NotificationEntry entry) {
+            Runnable pulseSupressedListener = () -> {
+                entry.setPulseSuppressed(true);
+                mNotificationIconAreaController.updateAodNotificationIcons();
+            };
             for (Callback callback : mCallbacks) {
-                callback.onNotificationAlerted();
+                callback.onNotificationAlerted(pulseSupressedListener);
             }
         }
 
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 5ce1329..3508c90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -70,7 +70,7 @@
  */
 public class StatusBarKeyguardViewManager implements RemoteInputController.Callback,
         StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener,
-        NotificationPanelView.PanelExpansionListener, NavigationModeController.ModeChangedListener {
+        PanelExpansionListener, NavigationModeController.ModeChangedListener {
 
     // When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
     private static final long HIDE_TIMING_CORRECTION_MS = - 16 * 3;
@@ -223,7 +223,7 @@
                 mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
                 mExpansionCallback, falsingManager);
         mNotificationPanelView = notificationPanelView;
-        notificationPanelView.setExpansionListener(this);
+        notificationPanelView.addExpansionListener(this);
         mBypassController = bypassController;
         mNotificationContainer = notificationContainer;
     }
@@ -561,18 +561,22 @@
             executeAfterKeyguardGoneAction();
             boolean wakeUnlockPulsing =
                     mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
-            if (wakeUnlockPulsing) {
+            boolean needsFading = needsBypassFading();
+            if (needsFading) {
+                delay = 0;
+                fadeoutDuration = KeyguardBypassController.BYPASS_PANEL_FADE_DURATION;
+            } else if (wakeUnlockPulsing) {
                 delay = 0;
                 fadeoutDuration = 240;
             }
-            mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
+            mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration, needsFading);
             mBiometricUnlockController.startKeyguardFadingAway();
             hideBouncer(true /* destroyView */);
             if (wakeUnlockPulsing) {
-                if (needsBypassFading()) {
+                if (needsFading) {
                     ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView,
                             mNotificationContainer,
-                            KeyguardBypassController.BYPASS_PANEL_FADE_DURATION,
+                            fadeoutDuration,
                                     () -> {
                         mStatusBar.hideKeyguard();
                         onKeyguardFadedAway();
@@ -585,10 +589,10 @@
                 boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
                 if (!staying) {
                     mStatusBarWindowController.setKeyguardFadingAway(true);
-                    if (needsBypassFading()) {
+                    if (needsFading) {
                         ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView,
                                 mNotificationContainer,
-                                KeyguardBypassController.BYPASS_PANEL_FADE_DURATION,
+                                fadeoutDuration,
                                 () -> {
                                     mStatusBar.hideKeyguard();
                                 });
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 4ddd0e9..d9a9f7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -51,12 +51,13 @@
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 
 import com.google.android.collect.Lists;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Field;
-
 import java.util.ArrayList;
+
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
@@ -352,7 +353,7 @@
     }
 
     private void applyForceStatusBarVisibleFlag(State state) {
-        if (state.forceStatusBarVisible) {
+        if (state.forceStatusBarVisible || state.forcePluginOpen) {
             mLpChanged.privateFlags |= WindowManager
                     .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index f1049f0..33b863f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -418,7 +418,7 @@
         if (mNotificationPanel.isFullyExpanded()
                 && mStatusBarStateController.getState() == StatusBarState.KEYGUARD
                 && !mService.isBouncerShowing()
-                && !mBypassController.getBypassEnabled()
+                && (!mBypassController.getBypassEnabled() || mNotificationPanel.isQsExpanded())
                 && !mService.isDozing()) {
             intercept = mDragDownHelper.onInterceptTouchEvent(ev);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
index a71fcdb..b1d6ca6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -28,6 +28,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 /**
@@ -51,7 +52,7 @@
     private boolean mTrustManaged;
     private boolean mTrusted;
     private boolean mDebugUnlocked = false;
-    private boolean mIsUnlockingWithFacePossible;
+    private boolean mFaceAuthEnabled;
 
     private UnlockMethodCache(Context ctx) {
         mLockPatternUtils = new LockPatternUtils(ctx);
@@ -110,8 +111,8 @@
     /**
      * If there are faces enrolled and user enabled face auth on keyguard.
      */
-    public boolean isUnlockingWithFacePossible() {
-        return mIsUnlockingWithFacePossible;
+    public boolean isFaceAuthEnabled() {
+        return mFaceAuthEnabled;
     }
 
     private void update(boolean updateAlways) {
@@ -122,16 +123,16 @@
                 || (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked);
         boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user);
         boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user);
-        boolean isUnlockingWithFacePossible = mKeyguardUpdateMonitor.isUnlockWithFacePossible(user);
+        boolean faceAuthEnabled = mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(user);
         boolean changed = secure != mSecure || canSkipBouncer != mCanSkipBouncer
                 || trustManaged != mTrustManaged
-                || mIsUnlockingWithFacePossible != isUnlockingWithFacePossible;
+                || mFaceAuthEnabled != faceAuthEnabled;
         if (changed || updateAlways) {
             mSecure = secure;
             mCanSkipBouncer = canSkipBouncer;
             mTrusted = trusted;
             mTrustManaged = trustManaged;
-            mIsUnlockingWithFacePossible = isUnlockingWithFacePossible;
+            mFaceAuthEnabled = faceAuthEnabled;
             notifyListeners();
         }
         Trace.endSection();
@@ -143,6 +144,16 @@
         }
     }
 
+    public void dump(PrintWriter pw) {
+        pw.println("UnlockMethodCache");
+        pw.println("  mSecure: " + mSecure);
+        pw.println("  mCanSkipBouncer: " + mCanSkipBouncer);
+        pw.println("  mTrustManaged: " + mTrustManaged);
+        pw.println("  mTrusted: " + mTrusted);
+        pw.println("  mDebugUnlocked: " + mDebugUnlocked);
+        pw.println("  mFaceAuthEnabled: " + mFaceAuthEnabled);
+    }
+
     private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
         @Override
         public void onUserSwitchComplete(int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
index f61b556..070136e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
@@ -29,6 +29,19 @@
     long getKeyguardFadingAwayDelay();
     long calculateGoingToFullShadeDelay();
 
+    /**
+     * @return a shortened fading away duration similar to
+     * {{@link #getKeyguardFadingAwayDuration()}} which may only span half of the duration, unless
+     * we're bypassing
+     */
+    default long getShortenedFadingAwayDuration() {
+        if (isBypassFadingAnimation()) {
+            return getKeyguardFadingAwayDuration();
+        } else {
+            return getKeyguardFadingAwayDuration() / 2;
+        }
+    }
+
     default boolean isDeviceInteractive() {
         return false;
     }
@@ -39,7 +52,21 @@
     default void notifyKeyguardGoingAway(boolean b) {
     }
 
-    default void notifyKeyguardFadingAway(long delay, long fadeoutDuration) {
+    /**
+     * @return {@code true} if the current fading away animation is the fast bypass fading.
+     */
+    default boolean isBypassFadingAnimation() {
+        return false;
+    }
+
+    /**
+     * Notifies that the Keyguard is fading away with the specified timings.
+     * @param delay the precalculated animation delay in milliseconds
+     * @param fadeoutDuration the duration of the exit animation, in milliseconds
+     * @param isBypassFading is this a fading away animation while bypassing
+     */
+    default void notifyKeyguardFadingAway(long delay, long fadeoutDuration,
+            boolean isBypassFading) {
     }
 
     default void notifyKeyguardDoneFading() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
index 68d0070..8829be4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
@@ -54,6 +54,7 @@
     private long mKeyguardFadingAwayDuration;
     private boolean mKeyguardGoingAway;
     private boolean mLaunchTransitionFadingAway;
+    private boolean mBypassFadingAnimation;
 
     /**
      */
@@ -140,10 +141,11 @@
         new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged);
     }
 
-    public void notifyKeyguardFadingAway(long delay, long fadeoutDuration) {
+    public void notifyKeyguardFadingAway(long delay, long fadeoutDuration, boolean isBypassFading) {
         setKeyguardFadingAway(true);
         mKeyguardFadingAwayDelay = delay;
         mKeyguardFadingAwayDuration = fadeoutDuration;
+        mBypassFadingAnimation = isBypassFading;
     }
 
     private void setKeyguardFadingAway(boolean keyguardFadingAway) {
@@ -172,6 +174,11 @@
     }
 
     @Override
+    public boolean isBypassFadingAnimation() {
+        return mBypassFadingAnimation;
+    }
+
+    @Override
     public long getKeyguardFadingAwayDelay() {
         return mKeyguardFadingAwayDelay;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 3f3e1e3..a6b5b38 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -43,6 +43,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -275,14 +276,14 @@
     }
 
     public boolean areCaptionsEnabled() {
-        int currentValue = Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.ODI_CAPTIONS_ENABLED, 0);
+        int currentValue = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                Settings.Secure.ODI_CAPTIONS_ENABLED, 0, UserHandle.USER_CURRENT);
         return currentValue == 1;
     }
 
     public void setCaptionsEnabled(boolean isEnabled) {
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.ODI_CAPTIONS_ENABLED, isEnabled ? 1 : 0);
+        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                Settings.Secure.ODI_CAPTIONS_ENABLED, isEnabled ? 1 : 0, UserHandle.USER_CURRENT);
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DumpControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/DumpControllerTest.kt
index cca35ca..d921d58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DumpControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/DumpControllerTest.kt
@@ -18,7 +18,6 @@
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -37,6 +36,7 @@
     private lateinit var controller: DumpController
     @Mock private lateinit var callback1: Dumpable
     @Mock private lateinit var callback2: Dumpable
+    @Mock private lateinit var callback3: Dumpable
     @Mock private lateinit var fd: FileDescriptor
     @Mock private lateinit var pw: PrintWriter
     private val args = emptyArray<String>()
@@ -46,26 +46,19 @@
         MockitoAnnotations.initMocks(this)
 
         controller = DumpController()
-//        Debug.waitForDebugger()
     }
 
-    @Test
+    @Test(expected = IllegalArgumentException::class)
     fun testListenerOnlyAddedOnce() {
-        controller.apply {
-            addListener(callback1)
-            addListener(callback1)
-        }
-        assertEquals(1, controller.numListeners)
-
-        controller.dump(fd, pw, args)
-        verify(callback1 /* only once */).dump(fd, pw, args)
+        controller.registerDumpable("cb1", callback1)
+        controller.registerDumpable("cb1", callback2)
     }
 
     @Test
     fun testListenersCalledOnDump() {
         controller.apply {
-            addListener(callback1)
-            addListener(callback2)
+            registerDumpable("cb1", callback1)
+            registerDumpable("cb2", callback2)
         }
 
         controller.dump(fd, pw, args)
@@ -75,11 +68,59 @@
     }
 
     @Test
+    fun testListenersAreFiltered() {
+        controller.apply {
+            registerDumpable("cb1", callback1)
+            registerDumpable("cb2", callback2)
+            registerDumpable("cb3", callback3)
+        }
+
+        val args = arrayOf("dependency", "DumpController", "cb3,cb1")
+        controller.dump(fd, pw, args)
+
+        verify(callback1 /* only once */).dump(fd, pw, args)
+        verify(callback2, never()).dump(fd, pw, args)
+        verify(callback3 /* only once */).dump(fd, pw, args)
+    }
+
+    @Test
+    fun testFiltersAreNotCaseSensitive() {
+        controller.apply {
+            registerDumpable("cb1", callback1)
+            registerDumpable("cb2", callback2)
+            registerDumpable("cb3", callback3)
+        }
+
+        val args = arrayOf("dependency", "DumpController", "CB3")
+        controller.dump(fd, pw, args)
+
+        verify(callback1, never()).dump(fd, pw, args)
+        verify(callback2, never()).dump(fd, pw, args)
+        verify(callback3 /* only once */).dump(fd, pw, args)
+    }
+
+    @Test
+    fun testFiltersAreIgnoredIfPrecedingArgsDontMatch() {
+        controller.apply {
+            registerDumpable("cb1", callback1)
+            registerDumpable("cb2", callback2)
+            registerDumpable("cb3", callback3)
+        }
+
+        val args = arrayOf("", "", "cb2")
+        controller.dump(fd, pw, args)
+
+        verify(callback1 /* only once */).dump(fd, pw, args)
+        verify(callback2 /* only once */).dump(fd, pw, args)
+        verify(callback3 /* only once */).dump(fd, pw, args)
+    }
+
+    @Test
     fun testRemoveListener() {
         controller.apply {
-            addListener(callback1)
-            addListener(callback2)
-            removeListener(callback1)
+            registerDumpable("cb1", callback1)
+            registerDumpable("cb2", callback2)
+            unregisterDumpable(callback1)
         }
 
         controller.dump(fd, pw, args)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index eb8ef09..d464223 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -95,13 +95,13 @@
         mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
         clearInvocations(mMachine);
 
-        mHost.callback.onNotificationAlerted();
+        mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
         mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */
 
         verify(mMachine, never()).requestState(any());
         verify(mMachine, never()).requestPulse(anyInt());
 
-        mHost.callback.onNotificationAlerted();
+        mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
         mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */
 
         verify(mMachine).requestPulse(anyInt());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index 893f3d1..9576cb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -48,6 +48,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -197,6 +198,7 @@
 
     @Test
     public void onMetadataChanged_updatesSlice() {
+        mProvider.onStateChanged(StatusBarState.KEYGUARD);
         mProvider.onDozingChanged(true);
         reset(mContentResolver);
         mProvider.onMetadataOrStateChanged(mock(MediaMetadata.class), PlaybackState.STATE_PLAYING);
@@ -210,6 +212,7 @@
 
     @Test
     public void onDozingChanged_updatesSliceIfMedia() {
+        mProvider.onStateChanged(StatusBarState.KEYGUARD);
         mProvider.onMetadataOrStateChanged(mock(MediaMetadata.class), PlaybackState.STATE_PLAYING);
         reset(mContentResolver);
         // Show media when dozing
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/recents/model/TaskKeyLruCacheTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/recents/model/TaskKeyLruCacheTest.java
new file mode 100644
index 0000000..eb71dd6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/recents/model/TaskKeyLruCacheTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.shared.recents.model;
+
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNull;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class TaskKeyLruCacheTest extends SysuiTestCase {
+    private static int sCacheSize = 3;
+    private static int sIdTask1 = 1;
+    private static int sIdTask2 = 2;
+    private static int sIdTask3 = 3;
+    private static int sIdUser1 = 1;
+
+    TaskKeyCache<Integer> mCache = new TaskKeyLruCache<>(sCacheSize, null);
+    Task.TaskKey mKey1;
+    Task.TaskKey mKey2;
+    Task.TaskKey mKey3;
+
+    @Before
+    public void setup() {
+        mKey1 = new Task.TaskKey(sIdTask1, 0, null, null, sIdUser1, System.currentTimeMillis());
+        mKey2 = new Task.TaskKey(sIdTask2, 0, null, null, sIdUser1, System.currentTimeMillis());
+        mKey3 = new Task.TaskKey(sIdTask3, 0, null, null, sIdUser1, System.currentTimeMillis());
+    }
+
+    @Test
+    public void addSingleItem_get_success() {
+        mCache.put(mKey1, 1);
+
+        assertEquals(1, (int) mCache.get(mKey1));
+    }
+
+    @Test
+    public void addSingleItem_getUninsertedItem_returnsNull() {
+        mCache.put(mKey1, 1);
+
+        assertNull(mCache.get(mKey2));
+    }
+
+    @Test
+    public void emptyCache_get_returnsNull() {
+        assertNull(mCache.get(mKey1));
+    }
+
+    @Test
+    public void updateItem_get_returnsSecond() {
+        mCache.put(mKey1, 1);
+        mCache.put(mKey1, 2);
+
+        assertEquals(2, (int) mCache.get(mKey1));
+        assertEquals(1, mCache.mKeys.size());
+    }
+
+    @Test
+    public void fillCache_put_evictsOldest() {
+        mCache.put(mKey1, 1);
+        mCache.put(mKey2, 2);
+        mCache.put(mKey3, 3);
+        Task.TaskKey key4 = new Task.TaskKey(sIdTask3 + 1, 0,
+                null, null, sIdUser1, System.currentTimeMillis());
+        mCache.put(key4, 4);
+
+        assertNull(mCache.get(mKey1));
+        assertEquals(3, mCache.mKeys.size());
+        assertEquals(mKey2, mCache.mKeys.valueAt(0));
+    }
+
+    @Test
+    public void fillCache_remove_success() {
+        mCache.put(mKey1, 1);
+        mCache.put(mKey2, 2);
+        mCache.put(mKey3, 3);
+
+        mCache.remove(mKey2);
+
+        assertNull(mCache.get(mKey2));
+        assertEquals(2, mCache.mKeys.size());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 49a263a..57dd8c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -38,7 +38,6 @@
 import android.os.Looper;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -48,6 +47,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
@@ -166,7 +166,7 @@
                 Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
         when(mNotificationData.isHighPriority(any())).thenReturn(false);
 
-        assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(mock(StatusBarNotification.class)));
+        assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(mock(NotificationEntry.class)));
     }
 
     @Test
@@ -179,7 +179,7 @@
                 Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
         when(mNotificationData.isHighPriority(any())).thenReturn(false);
 
-        assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(mock(StatusBarNotification.class)));
+        assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(mock(NotificationEntry.class)));
     }
 
     private class TestNotificationLockscreenUserManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 73abda9..59d0f91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -263,6 +263,8 @@
                     when(notifRow.getVisibility()).thenReturn(View.VISIBLE);
                     when(notifRow.getEntry().isHighPriority())
                             .thenReturn(children[i] == ChildType.HIPRI);
+                    when(notifRow.getEntry().isTopBucket())
+                            .thenReturn(children[i] == ChildType.HIPRI);
                     when(notifRow.getParent()).thenReturn(mNssl);
                     child = notifRow;
                     break;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 7d9920d..fd67611 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -82,7 +82,7 @@
         MockitoAnnotations.initMocks(this);
         when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
         when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
-        when(mUnlockMethodCache.isUnlockingWithFacePossible()).thenReturn(true);
+        when(mUnlockMethodCache.isFaceAuthEnabled()).thenReturn(true);
         when(mKeyguardBypassController.onBiometricAuthenticated(any())).thenReturn(true);
         when(mKeyguardBypassController.canPlaySubtleWindowAnimations()).thenReturn(true);
         mContext.addMockSystemService(PowerManager.class, mPowerManager);
@@ -161,6 +161,7 @@
 
     @Test
     public void onBiometricAuthenticated_whenFace_andBypass_encrypted_showBouncer() {
+        reset(mUpdateMonitor);
         when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
         mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
 
@@ -168,11 +169,18 @@
         mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
                 BiometricSourceType.FACE);
 
+        // Wake up before showing the bouncer
+        verify(mStatusBarKeyguardViewManager, never()).showBouncer(eq(false));
+        mBiometricUnlockController.mWakefulnessObserver.onFinishedWakingUp();
+
         verify(mStatusBarKeyguardViewManager).showBouncer(eq(false));
+        assertThat(mBiometricUnlockController.getMode())
+                .isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
     }
 
     @Test
     public void onBiometricAuthenticated_whenFace_noBypass_encrypted_doNothing() {
+        reset(mUpdateMonitor);
         mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
 
         when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(false);
@@ -181,6 +189,8 @@
 
         verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
         verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
+        assertThat(mBiometricUnlockController.getMode())
+                .isEqualTo(BiometricUnlockController.MODE_NONE);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index b45707e..a38094d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -35,6 +35,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
@@ -59,6 +60,7 @@
     private View mOperatorNameView;
     private StatusBarStateController mStatusbarStateController;
     private KeyguardBypassController mBypassController;
+    private NotificationWakeUpCoordinator mWakeUpCoordinator;
 
     @Before
     public void setUp() throws Exception {
@@ -72,11 +74,13 @@
         mOperatorNameView = new View(mContext);
         mStatusbarStateController = mock(StatusBarStateController.class);
         mBypassController = mock(KeyguardBypassController.class);
+        mWakeUpCoordinator = mock(NotificationWakeUpCoordinator.class);
         mHeadsUpAppearanceController = new HeadsUpAppearanceController(
                 mock(NotificationIconAreaController.class),
                 mHeadsUpManager,
                 mStatusbarStateController,
                 mBypassController,
+                mWakeUpCoordinator,
                 mHeadsUpStatusBarView,
                 mStackScroller,
                 mPanelView,
@@ -153,6 +157,7 @@
                 mHeadsUpManager,
                 mStatusbarStateController,
                 mBypassController,
+                mWakeUpCoordinator,
                 mHeadsUpStatusBarView,
                 mStackScroller,
                 mPanelView,
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 4e0ef56..907e695 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
@@ -379,7 +379,7 @@
 
     @Test
     public void testShow_delaysIfFaceAuthIsRunning() {
-        when(mUnlockMethodCache.isUnlockingWithFacePossible()).thenReturn(true);
+        when(mUnlockMethodCache.isFaceAuthEnabled()).thenReturn(true);
         mBouncer.show(true /* reset */);
 
         ArgumentCaptor<Runnable> showRunnable = ArgumentCaptor.forClass(Runnable.class);
@@ -394,4 +394,15 @@
     public void testRegisterUpdateMonitorCallback() {
         verify(mKeyguardUpdateMonitor).registerCallback(any());
     }
+
+    @Test
+    public void testInTransit_whenTranslation() {
+        mBouncer.show(true);
+        mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+        assertThat(mBouncer.inTransit()).isFalse();
+        mBouncer.setExpansion(0.5f);
+        assertThat(mBouncer.inTransit()).isTrue();
+        mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
+        assertThat(mBouncer.inTransit()).isFalse();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index d8e90a5..0dbf308 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -225,11 +225,12 @@
 
         mScrimController.transitionTo(ScrimState.PULSING);
         mScrimController.finishAnimationsImmediately();
-        // Front scrim should be transparent
+        // Front scrim should be transparent, but tinted
         // Back scrim should be semi-transparent so the user can see the wallpaper
         // Pulse callback should have been invoked
         assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
         assertScrimTint(mScrimBehind, true /* tinted */);
+        assertScrimTint(mScrimInFront, true /* tinted */);
 
         mScrimController.setWakeLockScreenSensorActive(true);
         mScrimController.finishAnimationsImmediately();
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index f9a2ca2..8ad2489 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -551,6 +551,30 @@
 
   // Histogram of the EAP method type of all installed Passpoint profiles for R2
   repeated PasspointProfileTypeCount installed_passpoint_profile_type_for_r2 = 148;
+
+  // Histogram of Tx link speed at 2G
+  repeated Int32Count tx_link_speed_count_2g = 149;
+
+  // Histogram of Tx link speed at 5G low band
+  repeated Int32Count tx_link_speed_count_5g_low = 150;
+
+  // Histogram of Tx link speed at 5G middle band
+  repeated Int32Count tx_link_speed_count_5g_mid = 151;
+
+  // Histogram of Tx link speed at 5G high band
+  repeated Int32Count tx_link_speed_count_5g_high = 152;
+
+  // Histogram of Rx link speed at 2G
+  repeated Int32Count rx_link_speed_count_2g = 153;
+
+  // Histogram of Rx link speed at 5G low band
+  repeated Int32Count rx_link_speed_count_5g_low = 154;
+
+  // Histogram of Rx link speed at 5G middle band
+  repeated Int32Count rx_link_speed_count_5g_mid = 155;
+
+  // Histogram of Rx link speed at 5G high band
+  repeated Int32Count rx_link_speed_count_5g_high = 156;
 }
 
 // Information that gets logged for every WiFi connection.
@@ -827,6 +851,7 @@
   optional int64 rssi_sum_of_squares_dbm_sq = 4;
 }
 
+
 // Number of occurrences of Soft AP session durations
 message SoftApDurationBucket {
   // Bucket covers duration : [duration_sec, duration_sec + bucket_size_sec)
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 2db7f0e..d923bed 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -23,6 +23,7 @@
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
 
+import android.accessibilityservice.AccessibilityGestureInfo;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
@@ -89,7 +90,7 @@
 
     protected final Context mContext;
     protected final SystemSupport mSystemSupport;
-    private final WindowManagerInternal mWindowManagerService;
+    protected final WindowManagerInternal mWindowManagerService;
     private final GlobalActionPerformer mGlobalActionPerformer;
     private final AccessibilityWindowManager mA11yWindowManager;
     private final DisplayManager mDisplayManager;
@@ -167,9 +168,10 @@
         @Nullable MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId);
 
         /**
-         * @return The current injector of motion events, if one exists
+         * @param displayId The display id.
+         * @return The current injector of motion events used on the display, if one exists.
          */
-        @Nullable MotionEventInjector getMotionEventInjectorLocked();
+        @Nullable MotionEventInjector getMotionEventInjectorForDisplayLocked(int displayId);
 
         /**
          * @return The current dispatcher for fingerprint gestures, if one exists
@@ -715,6 +717,10 @@
     }
 
     @Override
+    public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
+    }
+
+    @Override
     public boolean performAccessibilityAction(int accessibilityWindowId,
             long accessibilityNodeId, int action, Bundle arguments, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
@@ -1159,9 +1165,9 @@
         }
     }
 
-    public void notifyGesture(int gestureId) {
+    public void notifyGesture(AccessibilityGestureInfo gestureInfo) {
         mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE,
-                gestureId, 0).sendToTarget();
+                gestureInfo).sendToTarget();
     }
 
     public void notifyClearAccessibilityNodeInfoCache() {
@@ -1250,13 +1256,13 @@
         }
     }
 
-    private void notifyGestureInternal(int gestureId) {
+    private void notifyGestureInternal(AccessibilityGestureInfo gestureInfo) {
         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
         if (listener != null) {
             try {
-                listener.onGesture(gestureId);
+                listener.onGesture(gestureInfo);
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Error during sending gesture " + gestureId
+                Slog.e(LOG_TAG, "Error during sending gesture " + gestureInfo
                         + " to " + mService, re);
             }
         }
@@ -1451,8 +1457,7 @@
             final int type = message.what;
             switch (type) {
                 case MSG_ON_GESTURE: {
-                    final int gestureId = message.arg1;
-                    notifyGestureInternal(gestureId);
+                    notifyGestureInternal((AccessibilityGestureInfo) message.obj);
                 } break;
 
                 case MSG_CLEAR_ACCESSIBILITY_CACHE: {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
index d767011..b4ac92f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
@@ -16,21 +16,15 @@
 
 package com.android.server.accessibility;
 
+import android.accessibilityservice.AccessibilityGestureInfo;
 import android.accessibilityservice.AccessibilityService;
 import android.content.Context;
-import android.gesture.Gesture;
 import android.gesture.GesturePoint;
-import android.gesture.GestureStore;
-import android.gesture.GestureStroke;
-import android.gesture.Prediction;
 import android.graphics.PointF;
 import android.util.Slog;
 import android.util.TypedValue;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-import com.android.internal.R;
 
 import java.util.ArrayList;
 
@@ -125,11 +119,11 @@
         /**
          * Called when an event stream is recognized as a gesture.
          *
-         * @param gestureId ID of the gesture that was recognized.
+         * @param gestureInfo Information about the gesture.
          *
          * @return true if the event is consumed, else false
          */
-        boolean onGestureCompleted(int gestureId);
+        boolean onGestureCompleted(AccessibilityGestureInfo gestureInfo);
 
         /**
          * Called when the system has decided an event stream doesn't match any
@@ -562,6 +556,7 @@
     private boolean recognizeGesturePath(MotionEvent event, int policyFlags,
             ArrayList<PointF> path) {
 
+        final int displayId = event.getDisplayId();
         if (path.size() == 2) {
             PointF start = path.get(0);
             PointF end = path.get(1);
@@ -571,13 +566,21 @@
             int direction = toDirection(dX, dY);
             switch (direction) {
                 case LEFT:
-                    return mListener.onGestureCompleted(AccessibilityService.GESTURE_SWIPE_LEFT);
+                    return mListener.onGestureCompleted(
+                            new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_LEFT,
+                                    displayId));
                 case RIGHT:
-                    return mListener.onGestureCompleted(AccessibilityService.GESTURE_SWIPE_RIGHT);
+                    return mListener.onGestureCompleted(
+                            new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_RIGHT,
+                                    displayId));
                 case UP:
-                    return mListener.onGestureCompleted(AccessibilityService.GESTURE_SWIPE_UP);
+                    return mListener.onGestureCompleted(
+                            new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_UP,
+                                    displayId));
                 case DOWN:
-                    return mListener.onGestureCompleted(AccessibilityService.GESTURE_SWIPE_DOWN);
+                    return mListener.onGestureCompleted(
+                            new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_DOWN,
+                                    displayId));
                 default:
                     // Do nothing.
             }
@@ -596,7 +599,8 @@
             int segmentDirection0 = toDirection(dX0, dY0);
             int segmentDirection1 = toDirection(dX1, dY1);
             int gestureId = DIRECTIONS_TO_GESTURE_ID[segmentDirection0][segmentDirection1];
-            return mListener.onGestureCompleted(gestureId);
+            return mListener.onGestureCompleted(
+                    new AccessibilityGestureInfo(gestureId, displayId));
         }
         // else if (path.size() < 2 || 3 < path.size()) then no gesture recognized.
         return mListener.onGestureCancelled(event, policyFlags);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index b6cbbac..5111bec 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -114,7 +114,7 @@
     private final SparseArray<MagnificationGestureHandler> mMagnificationGestureHandler =
             new SparseArray<>(0);
 
-    private final SparseArray<MotionEventInjector> mMotionEventInjector = new SparseArray<>(0);
+    private final SparseArray<MotionEventInjector> mMotionEventInjectors = new SparseArray<>(0);
 
     private AutoclickController mAutoclickController;
 
@@ -412,12 +412,14 @@
                 MotionEventInjector injector = new MotionEventInjector(
                         mContext.getMainLooper());
                 addFirstEventHandler(displayId, injector);
-                // TODO: Need to set MotionEventInjector per display.
-                mAms.setMotionEventInjector(injector);
-                mMotionEventInjector.put(displayId, injector);
+                mMotionEventInjectors.put(displayId, injector);
             }
         }
 
+        if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
+            mAms.setMotionEventInjectors(mMotionEventInjectors);
+        }
+
         if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) {
             mKeyboardInterceptor = new KeyboardInterceptor(mAms,
                     LocalServices.getService(WindowManagerPolicy.class));
@@ -462,15 +464,14 @@
     }
 
     private void disableFeatures() {
-        for (int i = mMotionEventInjector.size() - 1; i >= 0; i--) {
-            final MotionEventInjector injector = mMotionEventInjector.valueAt(i);
-            // TODO: Need to set MotionEventInjector per display.
-            mAms.setMotionEventInjector(null);
+        for (int i = mMotionEventInjectors.size() - 1; i >= 0; i--) {
+            final MotionEventInjector injector = mMotionEventInjectors.valueAt(i);
             if (injector != null) {
                 injector.onDestroy();
             }
         }
-        mMotionEventInjector.clear();
+        mAms.setMotionEventInjectors(null);
+        mMotionEventInjectors.clear();
         if (mAutoclickController != null) {
             mAutoclickController.onDestroy();
             mAutoclickController = null;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index a220451..8148536 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -27,6 +27,7 @@
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.Manifest;
+import android.accessibilityservice.AccessibilityGestureInfo;
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
@@ -210,7 +211,7 @@
 
     private KeyEventDispatcher mKeyEventDispatcher;
 
-    private MotionEventInjector mMotionEventInjector;
+    private SparseArray<MotionEventInjector> mMotionEventInjectors;
 
     private FingerprintGestureDispatcher mFingerprintGestureDispatcher;
 
@@ -815,11 +816,11 @@
     }
 
 
-    boolean onGesture(int gestureId) {
+    boolean onGesture(AccessibilityGestureInfo gestureInfo) {
         synchronized (mLock) {
-            boolean handled = notifyGestureLocked(gestureId, false);
+            boolean handled = notifyGestureLocked(gestureInfo, false);
             if (!handled) {
-                handled = notifyGestureLocked(gestureId, true);
+                handled = notifyGestureLocked(gestureInfo, true);
             }
             return handled;
         }
@@ -860,30 +861,34 @@
      * Called by AccessibilityInputFilter when it creates or destroys the motionEventInjector.
      * Not using a getter because the AccessibilityInputFilter isn't thread-safe
      *
-     * @param motionEventInjector The new value of the motionEventInjector. May be null.
+     * @param motionEventInjectors The array of motionEventInjectors. May be null.
+     *
      */
-    void setMotionEventInjector(MotionEventInjector motionEventInjector) {
+    void setMotionEventInjectors(SparseArray<MotionEventInjector> motionEventInjectors) {
         synchronized (mLock) {
-            mMotionEventInjector = motionEventInjector;
+            mMotionEventInjectors = motionEventInjectors;
             // We may be waiting on this object being set
             mLock.notifyAll();
         }
     }
 
     @Override
-    public MotionEventInjector getMotionEventInjectorLocked() {
+    public @Nullable MotionEventInjector getMotionEventInjectorForDisplayLocked(int displayId) {
         final long endMillis = SystemClock.uptimeMillis() + WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS;
-        while ((mMotionEventInjector == null) && (SystemClock.uptimeMillis() < endMillis)) {
+        MotionEventInjector motionEventInjector = null;
+        while ((mMotionEventInjectors == null) && (SystemClock.uptimeMillis() < endMillis)) {
             try {
                 mLock.wait(endMillis - SystemClock.uptimeMillis());
             } catch (InterruptedException ie) {
                 /* ignore */
             }
         }
-        if (mMotionEventInjector == null) {
+        if (mMotionEventInjectors == null) {
             Slog.e(LOG_TAG, "MotionEventInjector installation timed out");
+        } else {
+            motionEventInjector = mMotionEventInjectors.get(displayId);
         }
-        return mMotionEventInjector;
+        return motionEventInjector;
     }
 
     /**
@@ -1010,7 +1015,7 @@
         }
     }
 
-    private boolean notifyGestureLocked(int gestureId, boolean isDefault) {
+    private boolean notifyGestureLocked(AccessibilityGestureInfo gestureInfo, boolean isDefault) {
         // TODO: Now we are giving the gestures to the last enabled
         //       service that can handle them which is the last one
         //       in our list since we write the last enabled as the
@@ -1024,7 +1029,7 @@
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             AccessibilityServiceConnection service = state.mBoundServices.get(i);
             if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) {
-                service.notifyGesture(gestureId);
+                service.notifyGesture(gestureInfo);
                 return true;
             }
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 02306c0..961168a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -238,7 +238,6 @@
         return (userState != null) ? userState.getSoftKeyboardShowMode() : 0;
     }
 
-
     @Override
     public boolean isAccessibilityButtonAvailable() {
         synchronized (mLock) {
@@ -354,12 +353,13 @@
     }
 
     @Override
-    public void sendGesture(int sequence, ParceledListSlice gestureSteps) {
+    public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
+        final boolean isTouchableDisplay = mWindowManagerService.isTouchableDisplay(displayId);
         synchronized (mLock) {
             if (mSecurityPolicy.canPerformGestures(this)) {
                 MotionEventInjector motionEventInjector =
-                        mSystemSupport.getMotionEventInjectorLocked();
-                if (motionEventInjector != null) {
+                        mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId);
+                if (motionEventInjector != null && isTouchableDisplay) {
                     motionEventInjector.injectEvents(
                             gestureSteps.getList(), mServiceInterface, sequence);
                 } else {
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 7920bbb..380e853 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -20,6 +20,7 @@
 
 import static com.android.server.accessibility.TouchState.ALL_POINTER_ID_BITS;
 
+import android.accessibilityservice.AccessibilityGestureInfo;
 import android.content.Context;
 import android.graphics.Point;
 import android.os.Handler;
@@ -356,14 +357,14 @@
     }
 
     @Override
-    public boolean onGestureCompleted(int gestureId) {
+    public boolean onGestureCompleted(AccessibilityGestureInfo gestureInfo) {
         if (!mState.isGestureDetecting()) {
             return false;
         }
 
         endGestureDetection(true);
 
-        mAms.onGesture(gestureId);
+        mAms.onGesture(gestureInfo);
 
         return true;
     }
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 73f5cb8..e2cdddb 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -165,7 +165,13 @@
         mComponentName = componentName;
         mCompatMode = compatMode;
 
-        context = new ContextThemeWrapper(context, mThemeId);
+        context = new ContextThemeWrapper(context, mThemeId) {
+            @Override
+            public void startActivity(Intent intent) {
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                super.startActivity(intent);
+            }
+        };
         final LayoutInflater inflater = LayoutInflater.from(context);
         final View view = inflater.inflate(R.layout.autofill_save, null);
 
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9855e4e..474dbfe 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -13,12 +13,14 @@
     },
     srcs: [
         "java/**/*.java",
+        ":platformcompat_aidl",
         ":dumpstate_aidl",
         ":idmap2_aidl",
         ":installd_aidl",
         ":storaged_aidl",
         ":vold_aidl",
         ":gsiservice_aidl",
+        ":platform-compat-config",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
         "java/com/android/server/policy/EventLogTags.logtags",
@@ -80,3 +82,11 @@
     name: "gps_debug.conf",
     src: "java/com/android/server/location/gps_debug.conf",
 }
+
+filegroup {
+    name: "platformcompat_aidl",
+    srcs: [
+        "java/com/android/server/compat/IPlatformCompat.aidl",
+    ],
+    path: "java",
+}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index ddfc3a6..a303718 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -1742,6 +1742,12 @@
             return mConstants;
         }
 
+
+        /** Returns the current elapsed realtime in milliseconds. */
+        long getElapsedRealtime() {
+            return SystemClock.elapsedRealtime();
+        }
+
         LocationManager getLocationManager() {
             if (mLocationManager == null) {
                 mLocationManager = mContext.getSystemService(LocationManager.class);
@@ -2023,7 +2029,7 @@
 
     private void unregisterDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint) {
         synchronized (this) {
-            // Artifically force the constraint to inactive to unblock anything waiting for it.
+            // Artificially force the constraint to inactive to unblock anything waiting for it.
             onConstraintStateChangedLocked(constraint, /* active= */ false);
 
             // Let the constraint know that we are not listening to it any more.
@@ -2746,9 +2752,18 @@
                 mState = STATE_QUICK_DOZE_DELAY;
                 // Make sure any motion sensing or locating is stopped.
                 resetIdleManagementLocked();
-                // Wait a small amount of time in case something (eg: background service from
-                // recently closed app) needs to finish running.
-                scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
+                if (isUpcomingAlarmClock()) {
+                    // If there's an upcoming AlarmClock alarm, we won't go into idle, so
+                    // setting a wakeup alarm before the upcoming alarm is futile. Set the quick
+                    // doze alarm to after the upcoming AlarmClock alarm.
+                    scheduleAlarmLocked(
+                            mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime()
+                                    + mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
+                } else {
+                    // Wait a small amount of time in case something (eg: background service from
+                    // recently closed app) needs to finish running.
+                    scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
+                }
                 EventLogTags.writeDeviceIdle(mState, "no activity");
             } else if (mState == STATE_ACTIVE) {
                 mState = STATE_INACTIVE;
@@ -2758,7 +2773,16 @@
                 if (shouldUseIdleTimeoutFactorLocked()) {
                     delay = (long) (mPreIdleFactor * delay);
                 }
-                scheduleAlarmLocked(delay, false);
+                if (isUpcomingAlarmClock()) {
+                    // If there's an upcoming AlarmClock alarm, we won't go into idle, so
+                    // setting a wakeup alarm before the upcoming alarm is futile. Set the idle
+                    // alarm to after the upcoming AlarmClock alarm.
+                    scheduleAlarmLocked(
+                            mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime()
+                                    + delay, false);
+                } else {
+                    scheduleAlarmLocked(delay, false);
+                }
                 EventLogTags.writeDeviceIdle(mState, "no activity");
             }
         }
@@ -2906,13 +2930,21 @@
         return mState;
     }
 
+    /**
+     * Returns true if there's an upcoming AlarmClock alarm that is soon enough to prevent the
+     * device from going into idle.
+     */
+    private boolean isUpcomingAlarmClock() {
+        return mInjector.getElapsedRealtime() + mConstants.MIN_TIME_TO_ALARM
+                >= mAlarmManager.getNextWakeFromIdleTime();
+    }
+
     @VisibleForTesting
     void stepIdleStateLocked(String reason) {
         if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
         EventLogTags.writeDeviceIdleStep();
 
-        final long now = SystemClock.elapsedRealtime();
-        if ((now+mConstants.MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {
+        if (isUpcomingAlarmClock()) {
             // Whoops, there is an upcoming alarm.  We don't actually want to go idle.
             if (mState != STATE_ACTIVE) {
                 mActiveReason = ACTIVE_REASON_ALARM;
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index f92d0e0..173d5b0 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -21,6 +21,7 @@
 import android.gsi.GsiInstallParams;
 import android.gsi.GsiProgress;
 import android.gsi.IGsiService;
+import android.gsi.IGsid;
 import android.os.Environment;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
@@ -61,7 +62,9 @@
          * re-initialized in this case.
          */
         binder.linkToDeath(recipient, 0);
-        return IGsiService.Stub.asInterface(binder);
+
+        IGsid gsid = IGsid.Stub.asInterface(binder);
+        return gsid.getClient();
     }
 
     /** implements DeathRecipient */
@@ -159,7 +162,7 @@
             isInUse = getGsiService().isGsiRunning();
         } finally {
             if (!gsidWasRunning && !isInUse) {
-                SystemProperties.set("ctl.stop", "gsid");
+                mGsiService = null;
             }
         }
 
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index f7e825e..e66e596 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1027,12 +1027,7 @@
                 log(str);
             }
             mLocalLog.log(str);
-            // for service state updates, don't notify clients when subId is invalid. This prevents
-            // us from sending incorrect notifications like b/133140128
-            // In the future, we can remove this logic for every notification here and add a
-            // callback so listeners know when their PhoneStateListener's subId becomes invalid, but
-            // for now we use the simplest fix.
-            if (validatePhoneId(phoneId) && SubscriptionManager.isValidSubscriptionId(subId)) {
+            if (validatePhoneId(phoneId)) {
                 mServiceState[phoneId] = state;
 
                 for (Record r : mRecords) {
@@ -1064,8 +1059,7 @@
                     }
                 }
             } else {
-                log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId
-                        + " or subId=" + subId);
+                log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId);
             }
             handleRemoveListLocked();
         }
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 30a3563..b9d7c68 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -48,7 +48,6 @@
 import android.os.ShellCommand;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.Settings.Secure;
 import android.service.dreams.Sandman;
 import android.service.vr.IVrManager;
@@ -218,6 +217,15 @@
         }
     };
 
+    private final ContentObserver mDarkThemeObserver = new ContentObserver(mHandler) {
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE,
+                    mNightMode, 0);
+            SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode));
+        }
+    };
+
     @Override
     public void onSwitchUser(int userHandle) {
         super.onSwitchUser(userHandle);
@@ -293,6 +301,9 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         context.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
+
+        context.getContentResolver().registerContentObserver(Secure.getUriFor(Secure.UI_NIGHT_MODE),
+                false, mDarkThemeObserver, 0);
     }
 
     // Records whether setup wizard has happened or not and adds an observer for this user if not.
@@ -417,11 +428,6 @@
                         if (!mCarModeEnabled) {
                             Secure.putIntForUser(getContext().getContentResolver(),
                                     Secure.UI_NIGHT_MODE, mode, user);
-
-                            if (UserManager.get(getContext()).isPrimaryUser()) {
-                                SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME,
-                                        Integer.toString(mode));
-                            }
                         }
 
                         mNightMode = mode;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 268e813..f3264e2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7901,11 +7901,12 @@
     }
 
     void reportGlobalUsageEventLocked(int event) {
-        mUsageStatsService.reportEvent("android", mUserController.getCurrentUserId(), event);
+        mUsageStatsService.reportEvent(Event.DEVICE_EVENT_PACKAGE_NAME,
+                mUserController.getCurrentUserId(), event);
         int[] profiles = mUserController.getCurrentProfileIds();
         if (profiles != null) {
             for (int i = profiles.length - 1; i >= 0; i--) {
-                mUsageStatsService.reportEvent((String)null, profiles[i], event);
+                mUsageStatsService.reportEvent(Event.DEVICE_EVENT_PACKAGE_NAME, profiles[i], event);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 33070dc..0dd7199 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -79,7 +79,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.TextUtils;
-import android.text.format.Time;
 import android.util.ArrayMap;
 import android.util.DebugUtils;
 import android.util.DisplayMetrics;
@@ -98,12 +97,16 @@
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.net.URISyntaxException;
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -116,10 +119,14 @@
 
 final class ActivityManagerShellCommand extends ShellCommand {
     public static final String NO_CLASS_ERROR_CODE = "Error type 3";
+
     private static final String SHELL_PACKAGE_NAME = "com.android.shell";
 
     private static final int USER_OPERATION_TIMEOUT_MS = 2 * 60 * 1000; // 2 minutes
 
+    private static final DateTimeFormatter LOG_NAME_TIME_FORMATTER =
+            DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss", Locale.ROOT);
+
     // IPC interface to activity manager -- don't need to do additional security checks.
     final IActivityManager mInterface;
     final IActivityTaskManager mTaskInterface;
@@ -922,9 +929,9 @@
         String process = getNextArgRequired();
         String heapFile = getNextArg();
         if (heapFile == null) {
-            final Time t = new Time();
-            t.set(System.currentTimeMillis());
-            heapFile = "/data/local/tmp/heapdump-" + t.format("%Y%m%d-%H%M%S") + ".prof";
+            LocalDateTime localDateTime = LocalDateTime.now(Clock.systemDefaultZone());
+            String logNameTimeString = LOG_NAME_TIME_FORMATTER.format(localDateTime);
+            heapFile = "/data/local/tmp/heapdump-" + logNameTimeString + ".prof";
         }
         pw.println("File: " + heapFile);
         pw.flush();
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 770cb3d..97e5293 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -34,6 +34,7 @@
 import static android.os.Process.THREAD_GROUP_DEFAULT;
 import static android.os.Process.THREAD_GROUP_RESTRICTED;
 import static android.os.Process.THREAD_GROUP_TOP_APP;
+import static android.os.Process.THREAD_PRIORITY_DISPLAY;
 import static android.os.Process.setProcessGroup;
 import static android.os.Process.setThreadPriority;
 import static android.os.Process.setThreadScheduler;
@@ -1815,7 +1816,6 @@
                                 if (app.renderThreadTid != 0) {
                                     setThreadScheduler(app.renderThreadTid,
                                             SCHED_OTHER, 0);
-                                    setThreadPriority(app.renderThreadTid, -4);
                                 }
                             } catch (IllegalArgumentException e) {
                                 Slog.w(TAG,
@@ -1827,9 +1827,10 @@
                         } else {
                             // Reset priority for top app UI and render threads
                             setThreadPriority(app.pid, 0);
-                            if (app.renderThreadTid != 0) {
-                                setThreadPriority(app.renderThreadTid, 0);
-                            }
+                        }
+
+                        if (app.renderThreadTid != 0) {
+                            setThreadPriority(app.renderThreadTid, THREAD_PRIORITY_DISPLAY);
                         }
                     }
                 } catch (Exception e) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index bd3cd54..af2f24f 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -789,6 +789,23 @@
             return error;
         }
 
+        @Override
+        public boolean hasEnrolledBiometrics(int userId) {
+            checkInternalPermission();
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                for (int i = 0; i < mAuthenticators.size(); i++) {
+                    if (mAuthenticators.get(i).mAuthenticator.hasEnrolledTemplates(userId)) {
+                        return true;
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+            return false;
+        }
+
         @Override // Binder call
         public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback)
                 throws RemoteException {
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 20eb618..f3f9754 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -282,10 +282,10 @@
         public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
                 IBinder token, ServiceListener listener, int userId, int groupId,
                 byte[] cryptoToken, boolean restricted, String owner,
-                final int[] disabledFeatures) {
+                final int[] disabledFeatures, int timeoutSec) {
             super(context, getConstants(), daemon, halDeviceId, token, listener,
                     userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(),
-                    disabledFeatures);
+                    disabledFeatures, timeoutSec);
         }
 
         @Override
@@ -912,8 +912,12 @@
     }
 
     protected void setActiveUserInternal(int userId) {
-        // Do not put on handler, since it should finish before returning to caller.
-        updateActiveGroup(userId, null /* clientPackage */);
+        mHandler.post(() -> {
+            if (DEBUG) {
+                Slog.d(getTag(), "setActiveUser(" + userId + ")");
+            }
+            updateActiveGroup(userId, null /* clientPackage */);
+        });
     }
 
     protected void removeInternal(RemovalClient client) {
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index 854528f..7ebb7c0 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -31,11 +31,11 @@
  * A class to keep track of the enrollment state for a given client.
  */
 public abstract class EnrollClient extends ClientMonitor {
-    private static final long MS_PER_SEC = 1000;
-    private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
     private final byte[] mCryptoToken;
     private final BiometricUtils mBiometricUtils;
     private final int[] mDisabledFeatures;
+    private final int mTimeoutSec;
+
     private long mEnrollmentStartTimeMs;
 
     public abstract boolean shouldVibrate();
@@ -44,12 +44,13 @@
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int userId, int groupId,
             byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils,
-            final int[] disabledFeatures) {
+            final int[] disabledFeatures, int timeoutSec) {
         super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted,
                 owner, 0 /* cookie */);
         mBiometricUtils = utils;
         mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
         mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
+        mTimeoutSec = timeoutSec;
     }
 
     @Override
@@ -94,14 +95,13 @@
     @Override
     public int start() {
         mEnrollmentStartTimeMs = System.currentTimeMillis();
-        final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
         try {
             final ArrayList<Integer> disabledFeatures = new ArrayList<>();
             for (int i = 0; i < mDisabledFeatures.length; i++) {
                 disabledFeatures.add(mDisabledFeatures[i]);
             }
 
-            final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), timeout,
+            final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), mTimeoutSec,
                     disabledFeatures);
             if (result != 0) {
                 Slog.w(getLogTag(), "startEnroll failed, result=" + result);
diff --git a/services/core/java/com/android/server/biometrics/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/LoggableMonitor.java
index 6c7cbc1..ecf3864 100644
--- a/services/core/java/com/android/server/biometrics/LoggableMonitor.java
+++ b/services/core/java/com/android/server/biometrics/LoggableMonitor.java
@@ -93,7 +93,7 @@
                 statsAction(),
                 statsClient(),
                 acquiredInfo,
-                0 /* vendorCode */, // Don't log vendorCode for now
+                vendorCode,
                 Utils.isDebugEnabled(context, targetUserId));
     }
 
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 a38abdc..a706521 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -329,6 +329,7 @@
      * Receives the incoming binder calls from FaceManager.
      */
     private final class FaceServiceWrapper extends IFaceService.Stub {
+        private static final int ENROLL_TIMEOUT_SEC = 75;
 
         /**
          * The following methods contain common code which is shared in biometrics/common.
@@ -343,16 +344,19 @@
         @Override // Binder call
         public int revokeChallenge(IBinder token) {
             checkPermission(MANAGE_BIOMETRIC);
-            // TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks.
-            if (getCurrentClient() == null) {
-                // if we aren't handling any other HIDL calls (mCurrentClient == null), revoke the
-                // challenge right away.
-                return startRevokeChallenge(token);
-            } else {
-                // postpone revoking the challenge until we finish processing the current HIDL call.
-                mRevokeChallengePending = true;
-                return Status.OK;
-            }
+            mHandler.post(() -> {
+                // TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks.
+                if (getCurrentClient() == null) {
+                    // if we aren't handling any other HIDL calls (mCurrentClient == null), revoke
+                    // the challenge right away.
+                    startRevokeChallenge(token);
+                } else {
+                    // postpone revoking the challenge until we finish processing the current HIDL
+                    // call.
+                    mRevokeChallengePending = true;
+                }
+            });
+            return Status.OK;
         }
 
         @Override // Binder call
@@ -368,7 +372,8 @@
             final boolean restricted = isRestricted();
             final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
                     mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId,
-                    0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures) {
+                    0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures,
+                    ENROLL_TIMEOUT_SEC) {
 
                 @Override
                 public int[] getAcquireIgnorelist() {
@@ -609,27 +614,32 @@
         public void resetLockout(byte[] token) {
             checkPermission(MANAGE_BIOMETRIC);
 
-            if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
-                Slog.w(TAG, "Ignoring lockout reset, no templates enrolled");
-                return;
-            }
+            mHandler.post(() -> {
+                if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
+                    Slog.w(TAG, "Ignoring lockout reset, no templates enrolled");
+                    return;
+                }
 
-            Slog.d(TAG, "Resetting lockout for user: " + mCurrentUserId);
+                Slog.d(TAG, "Resetting lockout for user: " + mCurrentUserId);
 
-            try {
-                mDaemonWrapper.resetLockout(token);
-            } catch (RemoteException e) {
-                Slog.e(getTag(), "Unable to reset lockout", e);
-            }
+                try {
+                    mDaemonWrapper.resetLockout(token);
+                } catch (RemoteException e) {
+                    Slog.e(getTag(), "Unable to reset lockout", e);
+                }
+            });
         }
 
         @Override
         public void setFeature(int userId, int feature, boolean enabled, final byte[] token,
                 IFaceServiceReceiver receiver, final String opPackageName) {
             checkPermission(MANAGE_BIOMETRIC);
-            updateActiveGroup(userId, opPackageName);
 
             mHandler.post(() -> {
+                if (DEBUG) {
+                    Slog.d(TAG, "setFeature for user(" + userId + ")");
+                }
+                updateActiveGroup(userId, opPackageName);
                 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
                     Slog.e(TAG, "No enrolled biometrics while setting feature: " + feature);
                     return;
@@ -660,9 +670,12 @@
         public void getFeature(int userId, int feature, IFaceServiceReceiver receiver,
                 final String opPackageName) {
             checkPermission(MANAGE_BIOMETRIC);
-            updateActiveGroup(userId, opPackageName);
 
             mHandler.post(() -> {
+                if (DEBUG) {
+                    Slog.d(TAG, "getFeature for user(" + userId + ")");
+                }
+                updateActiveGroup(userId, opPackageName);
                 // This should ideally return tri-state, but the user isn't shown settings unless
                 // they are enrolled so it's fine for now.
                 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 28336f4..320e102 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -176,6 +176,7 @@
      * Receives the incoming binder calls from FingerprintManager.
      */
     private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+        private static final int ENROLL_TIMEOUT_SEC = 60;
 
         /**
          * The following methods contain common code which is shared in biometrics/common.
@@ -203,7 +204,8 @@
             final int groupId = userId; // default group for fingerprint enrollment
             final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
                     mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId,
-                    cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */) {
+                    cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */,
+                    ENROLL_TIMEOUT_SEC) {
                 @Override
                 public boolean shouldVibrate() {
                     return true;
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index 9730c9a..1a1845a 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -28,7 +28,6 @@
 import android.hardware.broadcastradio.V2_0.ProgramFilter;
 import android.hardware.broadcastradio.V2_0.ProgramIdentifier;
 import android.hardware.broadcastradio.V2_0.ProgramInfo;
-import android.hardware.broadcastradio.V2_0.ProgramInfoFlags;
 import android.hardware.broadcastradio.V2_0.ProgramListChunk;
 import android.hardware.broadcastradio.V2_0.Properties;
 import android.hardware.broadcastradio.V2_0.Result;
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java b/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java
index b1bd395..8c93891 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java
@@ -48,6 +48,10 @@
     private final Map<ProgramSelector.Identifier, RadioManager.ProgramInfo> mProgramInfoMap =
             new HashMap<>();
 
+    // Flag indicating whether mProgramInfoMap is considered complete based upon the received
+    // updates.
+    private boolean mComplete = true;
+
     // Optional filter used in filterAndUpdateFrom(). Usually this field is null for a HAL-side
     // cache and non-null for an AIDL-side cache.
     private final ProgramList.Filter mFilter;
@@ -58,9 +62,10 @@
 
     // Constructor for testing.
     @VisibleForTesting
-    ProgramInfoCache(@Nullable ProgramList.Filter filter,
+    ProgramInfoCache(@Nullable ProgramList.Filter filter, boolean complete,
             RadioManager.ProgramInfo... programInfos) {
         mFilter = filter;
+        mComplete = complete;
         for (RadioManager.ProgramInfo programInfo : programInfos) {
             mProgramInfoMap.put(programInfo.getSelector().getPrimaryId(), programInfo);
         }
@@ -77,15 +82,23 @@
 
     @Override
     public String toString() {
-        StringBuilder sb = new StringBuilder("ProgramInfoCache(");
+        StringBuilder sb = new StringBuilder("ProgramInfoCache(mComplete = ");
+        sb.append(mComplete);
+        sb.append(", mFilter = ");
+        sb.append(mFilter);
+        sb.append(", mProgramInfoMap = [");
         mProgramInfoMap.forEach((id, programInfo) -> {
             sb.append("\n");
             sb.append(programInfo.toString());
         });
-        sb.append(")");
+        sb.append("]");
         return sb.toString();
     }
 
+    public boolean isComplete() {
+        return mComplete;
+    }
+
     public @Nullable ProgramList.Filter getFilter() {
         return mFilter;
     }
@@ -102,6 +115,7 @@
         for (android.hardware.broadcastradio.V2_0.ProgramIdentifier halProgramId : chunk.removed) {
             mProgramInfoMap.remove(Convert.programIdentifierFromHal(halProgramId));
         }
+        mComplete = chunk.complete;
     }
 
     @NonNull List<ProgramList.Chunk> filterAndUpdateFrom(@NonNull ProgramInfoCache other,
@@ -122,26 +136,18 @@
             purge = true;
         }
 
-        Set<Integer> idTypes = mFilter != null ? mFilter.getIdentifierTypes() : null;
-        Set<ProgramSelector.Identifier> ids = mFilter != null ? mFilter.getIdentifiers() : null;
-        boolean includeCategories = mFilter != null ? mFilter.areCategoriesIncluded() : true;
-        boolean includeModifications = mFilter != null ? !mFilter.areModificationsExcluded() : true;
-
         Set<RadioManager.ProgramInfo> modified = new HashSet<>();
         Set<ProgramSelector.Identifier> removed = new HashSet<>(mProgramInfoMap.keySet());
         for (Map.Entry<ProgramSelector.Identifier, RadioManager.ProgramInfo> entry
                 : other.mProgramInfoMap.entrySet()) {
             ProgramSelector.Identifier id = entry.getKey();
-            if ((idTypes != null && !idTypes.isEmpty() && !idTypes.contains(id.getType()))
-                    || (ids != null && !ids.isEmpty() && !ids.contains(id))
-                    || (!includeCategories && id.isCategoryType())) {
+            if (!passesFilter(id)) {
                 continue;
             }
-
             removed.remove(id);
-            RadioManager.ProgramInfo oldInfo = mProgramInfoMap.get(id);
+
             RadioManager.ProgramInfo newInfo = entry.getValue();
-            if (oldInfo != null && (!includeModifications || oldInfo.equals(newInfo))) {
+            if (!shouldIncludeInModified(newInfo)) {
                 continue;
             }
             mProgramInfoMap.put(id, newInfo);
@@ -150,14 +156,81 @@
         for (ProgramSelector.Identifier rem : removed) {
             mProgramInfoMap.remove(rem);
         }
-        return buildChunks(purge, modified, maxNumModifiedPerChunk, removed, maxNumRemovedPerChunk);
+        mComplete = other.mComplete;
+        return buildChunks(purge, mComplete, modified, maxNumModifiedPerChunk, removed,
+                maxNumRemovedPerChunk);
+    }
+
+    @Nullable List<ProgramList.Chunk> filterAndApplyChunk(@NonNull ProgramList.Chunk chunk) {
+        return filterAndApplyChunkInternal(chunk, MAX_NUM_MODIFIED_PER_CHUNK,
+                MAX_NUM_REMOVED_PER_CHUNK);
+    }
+
+    @VisibleForTesting
+    @Nullable List<ProgramList.Chunk> filterAndApplyChunkInternal(@NonNull ProgramList.Chunk chunk,
+            int maxNumModifiedPerChunk, int maxNumRemovedPerChunk) {
+        if (chunk.isPurge()) {
+            mProgramInfoMap.clear();
+        }
+
+        Set<RadioManager.ProgramInfo> modified = new HashSet<>();
+        Set<ProgramSelector.Identifier> removed = new HashSet<>();
+        for (RadioManager.ProgramInfo info : chunk.getModified()) {
+            ProgramSelector.Identifier id = info.getSelector().getPrimaryId();
+            if (!passesFilter(id) || !shouldIncludeInModified(info)) {
+                continue;
+            }
+            mProgramInfoMap.put(id, info);
+            modified.add(info);
+        }
+        for (ProgramSelector.Identifier id : chunk.getRemoved()) {
+            if (mProgramInfoMap.containsKey(id)) {
+                mProgramInfoMap.remove(id);
+                removed.add(id);
+            }
+        }
+        if (modified.isEmpty() && removed.isEmpty() && mComplete == chunk.isComplete()) {
+            return null;
+        }
+        mComplete = chunk.isComplete();
+        return buildChunks(chunk.isPurge(), mComplete, modified, maxNumModifiedPerChunk, removed,
+                maxNumRemovedPerChunk);
+    }
+
+    private boolean passesFilter(ProgramSelector.Identifier id) {
+        if (mFilter == null) {
+            return true;
+        }
+        if (!mFilter.getIdentifierTypes().isEmpty()
+                && !mFilter.getIdentifierTypes().contains(id.getType())) {
+            return false;
+        }
+        if (!mFilter.getIdentifiers().isEmpty() && !mFilter.getIdentifiers().contains(id)) {
+            return false;
+        }
+        if (!mFilter.areCategoriesIncluded() && id.isCategoryType()) {
+            return false;
+        }
+        return true;
+    }
+
+    private boolean shouldIncludeInModified(RadioManager.ProgramInfo newInfo) {
+        RadioManager.ProgramInfo oldInfo = mProgramInfoMap.get(
+                newInfo.getSelector().getPrimaryId());
+        if (oldInfo == null) {
+            return true;
+        }
+        if (mFilter != null && mFilter.areModificationsExcluded()) {
+            return false;
+        }
+        return !oldInfo.equals(newInfo);
     }
 
     private static int roundUpFraction(int numerator, int denominator) {
         return (numerator / denominator) + (numerator % denominator > 0 ? 1 : 0);
     }
 
-    private @NonNull List<ProgramList.Chunk> buildChunks(boolean purge,
+    private static @NonNull List<ProgramList.Chunk> buildChunks(boolean purge, boolean complete,
             @Nullable Collection<RadioManager.ProgramInfo> modified, int maxNumModifiedPerChunk,
             @Nullable Collection<ProgramSelector.Identifier> removed, int maxNumRemovedPerChunk) {
         // Communication protocol requires that if purge is set, removed is empty.
@@ -205,8 +278,8 @@
                     removedChunk.add(removedIter.next());
                 }
             }
-            chunks.add(new ProgramList.Chunk(purge && i == 0, i == numChunks - 1, modifiedChunk,
-                      removedChunk));
+            chunks.add(new ProgramList.Chunk(purge && i == 0, complete && (i == numChunks - 1),
+                      modifiedChunk, removedChunk));
         }
         return chunks;
     }
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index acb0207..53890a4 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -40,6 +40,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -64,7 +65,13 @@
     private Boolean mAntennaConnected = null;
 
     @GuardedBy("mLock")
-    private RadioManager.ProgramInfo mProgramInfo = null;
+    private RadioManager.ProgramInfo mCurrentProgramInfo = null;
+
+    @GuardedBy("mLock")
+    private final ProgramInfoCache mProgramInfoCache = new ProgramInfoCache(null);
+
+    @GuardedBy("mLock")
+    private android.hardware.radio.ProgramList.Filter mUnionOfAidlProgramFilters = null;
 
     // Callback registered with the HAL to relay callbacks to AIDL clients.
     private final ITunerCallback mHalTunerCallback = new ITunerCallback.Stub() {
@@ -78,17 +85,22 @@
         public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) {
             RadioManager.ProgramInfo programInfo = Convert.programInfoFromHal(halProgramInfo);
             synchronized (mLock) {
-                mProgramInfo = programInfo;
+                mCurrentProgramInfo = programInfo;
                 fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(programInfo));
             }
         }
 
         @Override
         public void onProgramListUpdated(ProgramListChunk programListChunk) {
-            // TODO: Cache per-AIDL client filters, send union of filters to HAL, use filters to fan
-            // back out to clients.
-            fanoutAidlCallback(cb -> cb.onProgramListUpdated(Convert.programListChunkFromHal(
-                    programListChunk)));
+            synchronized (mLock) {
+                android.hardware.radio.ProgramList.Chunk chunk =
+                        Convert.programListChunkFromHal(programListChunk);
+                mProgramInfoCache.filterAndApplyChunk(chunk);
+
+                for (TunerSession tunerSession : mAidlTunerSessions) {
+                    tunerSession.onMergedProgramListUpdateFromHal(chunk);
+                }
+            }
         }
 
         @Override
@@ -109,8 +121,9 @@
     @GuardedBy("mLock")
     private final Set<TunerSession> mAidlTunerSessions = new HashSet<>();
 
-    private RadioModule(@NonNull IBroadcastRadio service,
-            @NonNull RadioManager.ModuleProperties properties) throws RemoteException {
+    @VisibleForTesting
+    RadioModule(@NonNull IBroadcastRadio service,
+            @NonNull RadioManager.ModuleProperties properties) {
         mProperties = Objects.requireNonNull(properties);
         mService = Objects.requireNonNull(service);
     }
@@ -163,8 +176,8 @@
             if (mAntennaConnected != null) {
                 userCb.onAntennaState(mAntennaConnected);
             }
-            if (mProgramInfo != null) {
-                userCb.onCurrentProgramInfoChanged(mProgramInfo);
+            if (mCurrentProgramInfo != null) {
+                userCb.onCurrentProgramInfoChanged(mCurrentProgramInfo);
             }
 
             return tunerSession;
@@ -186,18 +199,114 @@
         }
     }
 
+    private @Nullable android.hardware.radio.ProgramList.Filter
+            buildUnionOfTunerSessionFiltersLocked() {
+        Set<Integer> idTypes = null;
+        Set<android.hardware.radio.ProgramSelector.Identifier> ids = null;
+        boolean includeCategories = false;
+        boolean excludeModifications = true;
+
+        for (TunerSession tunerSession : mAidlTunerSessions) {
+            android.hardware.radio.ProgramList.Filter filter =
+                    tunerSession.getProgramListFilter();
+            if (filter == null) {
+                continue;
+            }
+
+            if (idTypes == null) {
+                idTypes = new HashSet<>(filter.getIdentifierTypes());
+                ids = new HashSet<>(filter.getIdentifiers());
+                includeCategories = filter.areCategoriesIncluded();
+                excludeModifications = filter.areModificationsExcluded();
+                continue;
+            }
+            if (!idTypes.isEmpty()) {
+                if (filter.getIdentifierTypes().isEmpty()) {
+                    idTypes.clear();
+                } else {
+                    idTypes.addAll(filter.getIdentifierTypes());
+                }
+            }
+
+            if (!ids.isEmpty()) {
+                if (filter.getIdentifiers().isEmpty()) {
+                    ids.clear();
+                } else {
+                    ids.addAll(filter.getIdentifiers());
+                }
+            }
+
+            includeCategories |= filter.areCategoriesIncluded();
+            excludeModifications &= filter.areModificationsExcluded();
+        }
+
+        return idTypes == null ? null : new android.hardware.radio.ProgramList.Filter(idTypes, ids,
+                includeCategories, excludeModifications);
+    }
+
+    void onTunerSessionProgramListFilterChanged(@Nullable TunerSession session) {
+        synchronized (mLock) {
+            onTunerSessionProgramListFilterChangedLocked(session);
+        }
+    }
+
+    private void onTunerSessionProgramListFilterChangedLocked(@Nullable TunerSession session) {
+        android.hardware.radio.ProgramList.Filter newFilter =
+                buildUnionOfTunerSessionFiltersLocked();
+        if (newFilter == null) {
+            // If there are no AIDL clients remaining, we can stop updates from the HAL as well.
+            if (mUnionOfAidlProgramFilters == null) {
+                return;
+            }
+            mUnionOfAidlProgramFilters = null;
+            try {
+                mHalTunerSession.stopProgramListUpdates();
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "mHalTunerSession.stopProgramListUpdates() failed: ", ex);
+            }
+            return;
+        }
+
+        // If the HAL filter doesn't change, we can immediately send an update to the AIDL
+        // client.
+        if (newFilter.equals(mUnionOfAidlProgramFilters)) {
+            if (session != null) {
+                session.updateProgramInfoFromHalCache(mProgramInfoCache);
+            }
+            return;
+        }
+
+        // Otherwise, update the HAL's filter, and AIDL clients will be updated when
+        // mHalTunerCallback.onProgramListUpdated() is called.
+        mUnionOfAidlProgramFilters = newFilter;
+        try {
+            int halResult = mHalTunerSession.startProgramListUpdates(Convert.programFilterToHal(
+                    newFilter));
+            Convert.throwOnError("startProgramListUpdates", halResult);
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "mHalTunerSession.startProgramListUpdates() failed: ", ex);
+        }
+    }
+
     void onTunerSessionClosed(TunerSession tunerSession) {
         synchronized (mLock) {
+            onTunerSessionsClosedLocked(tunerSession);
+        }
+    }
+
+    private void onTunerSessionsClosedLocked(TunerSession... tunerSessions) {
+        for (TunerSession tunerSession : tunerSessions) {
             mAidlTunerSessions.remove(tunerSession);
-            if (mAidlTunerSessions.isEmpty() && mHalTunerSession != null) {
-                Slog.v(TAG, "closing HAL tuner session");
-                try {
-                    mHalTunerSession.close();
-                } catch (RemoteException ex) {
-                    Slog.e(TAG, "mHalTunerSession.close() failed: ", ex);
-                }
-                mHalTunerSession = null;
+        }
+        onTunerSessionProgramListFilterChanged(null);
+        if (mAidlTunerSessions.isEmpty() && mHalTunerSession != null) {
+            Slog.v(TAG, "closing HAL tuner session");
+            try {
+                mHalTunerSession.close();
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "mHalTunerSession.close() failed: ", ex);
             }
+            mHalTunerSession = null;
         }
     }
 
@@ -213,18 +322,25 @@
     }
 
     private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
+        List<TunerSession> deadSessions = null;
         for (TunerSession tunerSession : mAidlTunerSessions) {
             try {
                 runnable.run(tunerSession.mCallback);
             } catch (DeadObjectException ex) {
-                // The other side died without calling close(), so just purge it from our
-                // records.
+                // The other side died without calling close(), so just purge it from our records.
                 Slog.e(TAG, "Removing dead TunerSession");
-                mAidlTunerSessions.remove(tunerSession);
+                if (deadSessions == null) {
+                    deadSessions = new ArrayList<>();
+                }
+                deadSessions.add(tunerSession);
             } catch (RemoteException ex) {
                 Slog.e(TAG, "Failed to invoke ITunerCallback: ", ex);
             }
         }
+        if (deadSessions != null) {
+            onTunerSessionsClosedLocked(deadSessions.toArray(
+                    new TunerSession[deadSessions.size()]));
+        }
     }
 
     public android.hardware.radio.ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 008fea5..764fca9 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -46,6 +46,7 @@
     final android.hardware.radio.ITunerCallback mCallback;
     private boolean mIsClosed = false;
     private boolean mIsMuted = false;
+    private ProgramInfoCache mProgramInfoCache = null;
 
     // necessary only for older APIs compatibility
     private RadioManager.BandConfig mDummyConfig = null;
@@ -187,8 +188,51 @@
     public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException {
         synchronized (mLock) {
             checkNotClosedLocked();
-            int halResult = mHwSession.startProgramListUpdates(Convert.programFilterToHal(filter));
-            Convert.throwOnError("startProgramListUpdates", halResult);
+            mProgramInfoCache = new ProgramInfoCache(filter);
+        }
+        // Note: RadioModule.onTunerSessionProgramListFilterChanged() must be called without mLock
+        // held since it can call getProgramListFilter() and onHalProgramInfoUpdated().
+        mModule.onTunerSessionProgramListFilterChanged(this);
+    }
+
+    ProgramList.Filter getProgramListFilter() {
+        synchronized (mLock) {
+            return mProgramInfoCache == null ? null : mProgramInfoCache.getFilter();
+        }
+    }
+
+    void onMergedProgramListUpdateFromHal(ProgramList.Chunk mergedChunk) {
+        List<ProgramList.Chunk> clientUpdateChunks = null;
+        synchronized (mLock) {
+            if (mProgramInfoCache == null) {
+                return;
+            }
+            clientUpdateChunks = mProgramInfoCache.filterAndApplyChunk(mergedChunk);
+        }
+        dispatchClientUpdateChunks(clientUpdateChunks);
+    }
+
+    void updateProgramInfoFromHalCache(ProgramInfoCache halCache) {
+        List<ProgramList.Chunk> clientUpdateChunks = null;
+        synchronized (mLock) {
+            if (mProgramInfoCache == null) {
+                return;
+            }
+            clientUpdateChunks = mProgramInfoCache.filterAndUpdateFrom(halCache, true);
+        }
+        dispatchClientUpdateChunks(clientUpdateChunks);
+    }
+
+    private void dispatchClientUpdateChunks(@Nullable List<ProgramList.Chunk> chunks) {
+        if (chunks == null) {
+            return;
+        }
+        for (ProgramList.Chunk chunk : chunks) {
+            try {
+                mCallback.onProgramListUpdated(chunk);
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "mCallback.onProgramListUpdated() failed: ", ex);
+            }
         }
     }
 
@@ -196,8 +240,11 @@
     public void stopProgramListUpdates() throws RemoteException {
         synchronized (mLock) {
             checkNotClosedLocked();
-            mHwSession.stopProgramListUpdates();
+            mProgramInfoCache = null;
         }
+        // Note: RadioModule.onTunerSessionProgramListFilterChanged() must be called without mLock
+        // held since it can call getProgramListFilter() and onHalProgramInfoUpdated().
+        mModule.onTunerSessionProgramListFilterChanged(this);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 2a866f3..6f32bee 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -20,6 +20,8 @@
 import android.compat.annotation.EnabledAfter;
 import android.content.pm.ApplicationInfo;
 
+import com.android.server.compat.config.Change;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -60,6 +62,16 @@
         mDisabled = disabled;
     }
 
+    /**
+     * @param change an object generated by services/core/xsd/platform-compat-config.xsd
+     */
+    public CompatChange(Change change) {
+        mChangeId = change.getId();
+        mName = change.getName();
+        mEnableAfterTargetSdk = change.getEnableAfterTargetSdk();
+        mDisabled = change.getDisabled();
+    }
+
     long getId() {
         return mChangeId;
     }
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index bcf1d80..044e417 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -17,13 +17,27 @@
 package com.android.server.compat;
 
 import android.content.pm.ApplicationInfo;
+import android.os.Environment;
 import android.text.TextUtils;
 import android.util.LongArray;
 import android.util.LongSparseArray;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.compat.config.Change;
+import com.android.server.compat.config.XmlParser;
 
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+
+import javax.xml.datatype.DatatypeConfigurationException;
 /**
  * This class maintains state relating to platform compatibility changes.
  *
@@ -32,7 +46,12 @@
  */
 public final class CompatConfig {
 
-    private static final CompatConfig sInstance = new CompatConfig();
+    private static final String TAG = "CompatConfig";
+    private static final String CONFIG_FILE_SUFFIX = "platform_compat_config.xml";
+
+    private static final CompatConfig sInstance = new CompatConfig().initConfigFromLib(
+            Environment.buildPath(
+                    Environment.getRootDirectory(), "etc", "sysconfig"));
 
     @GuardedBy("mChanges")
     private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
@@ -169,4 +188,47 @@
         return overrideExists;
     }
 
+    /**
+    * Dumps the current list of compatibility config information.
+    *
+    * @param pw The {@link PrintWriter} instance to which the information will be dumped.
+    */
+    public void dumpConfig(PrintWriter pw) {
+        synchronized (mChanges) {
+            if (mChanges.size() == 0) {
+                pw.println("No compat overrides.");
+                return;
+            }
+            for (int i = 0; i < mChanges.size(); ++i) {
+                CompatChange c = mChanges.valueAt(i);
+                pw.println(c.toString());
+            }
+        }
+    }
+
+    CompatConfig initConfigFromLib(File libraryDir) {
+        if (!libraryDir.exists() || !libraryDir.isDirectory()) {
+            Slog.e(TAG, "No directory " + libraryDir + ", skipping");
+            return this;
+        }
+        for (File f : libraryDir.listFiles()) {
+            //TODO(b/138222363): Handle duplicate ids across config files.
+            if (f.getPath().endsWith(CONFIG_FILE_SUFFIX)) {
+                readConfig(f);
+            }
+        }
+        return this;
+    }
+
+    private void readConfig(File configFile) {
+        try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
+            for (Change change : XmlParser.read(in).getCompatChange()) {
+                Slog.w(TAG, "Adding: " + change.toString());
+                addChange(new CompatChange(change));
+            }
+        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+            Slog.e(TAG, "Encountered an error while reading/parsing compat config file", e);
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/compat/IPlatformCompat.aidl b/services/core/java/com/android/server/compat/IPlatformCompat.aidl
new file mode 100644
index 0000000..8ab08f9
--- /dev/null
+++ b/services/core/java/com/android/server/compat/IPlatformCompat.aidl
@@ -0,0 +1,57 @@
+/*
+ * 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.compat;
+
+import android.content.pm.ApplicationInfo;
+
+/**
+ * System private API for talking with the PlatformCompat service.
+ * {@hide}
+ */
+interface IPlatformCompat
+{
+
+    /**
+     * Reports that a compatibility change is affecting an app process now.
+     *
+     * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)},
+     * you do not need to call this API directly. The change will be reported for you in the case
+     * that {@link #isChangeEnabled(long, ApplicationInfo)} returns {@code true}.
+     *
+     * @param changeId The ID of the compatibility change taking effect.
+     * @param appInfo Representing the affected app.
+     */
+    void reportChange(long changeId, in ApplicationInfo appInfo);
+
+    /**
+     * Query if a given compatibility change is enabled for an app process. This method should
+     * be called when implementing functionality on behalf of the affected app.
+     *
+     * <p>If this method returns {@code true}, the calling code should implement the compatibility
+     * change, resulting in differing behaviour compared to earlier releases. If this method returns
+     * {@code false}, the calling code should behave as it did in earlier releases.
+     *
+     * <p>When this method returns {@code true}, it will also report the change as
+     * {@link #reportChange(long, ApplicationInfo)} would, so there is no need to call that method
+     * directly.
+     *
+     * @param changeId The ID of the compatibility change in question.
+     * @param appInfo Representing the app in question.
+     * @return {@code true} if the change is enabled for the current app.
+     */
+    boolean isChangeEnabled(long changeId, in ApplicationInfo appInfo);
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 456d15e..3eea194 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -16,52 +16,46 @@
 
 package com.android.server.compat;
 
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.util.Slog;
 
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 /**
  * System server internal API for gating and reporting compatibility changes.
  */
-public class PlatformCompat {
+public class PlatformCompat extends IPlatformCompat.Stub {
 
     private static final String TAG = "Compatibility";
 
-    /**
-     * Reports that a compatibility change is affecting an app process now.
-     *
-     * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)},
-     * you do not need to call this API directly. The change will be reported for you in the case
-     * that {@link #isChangeEnabled(long, ApplicationInfo)} returns {@code true}.
-     *
-     * @param changeId The ID of the compatibility change taking effect.
-     * @param appInfo Representing the affected app.
-     */
-    public static void reportChange(long changeId, ApplicationInfo appInfo) {
+    private final Context mContext;
+
+    public PlatformCompat(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public void reportChange(long changeId, ApplicationInfo appInfo) {
         Slog.d(TAG, "Compat change reported: " + changeId + "; UID " + appInfo.uid);
         // TODO log via StatsLog
     }
 
-    /**
-     * Query if a given compatibility change is enabled for an app process. This method should
-     * be called when implementing functionality on behalf of the affected app.
-     *
-     * <p>If this method returns {@code true}, the calling code should implement the compatibility
-     * change, resulting in differing behaviour compared to earlier releases. If this method returns
-     * {@code false}, the calling code should behave as it did in earlier releases.
-     *
-     * <p>When this method returns {@code true}, it will also report the change as
-     * {@link #reportChange(long, ApplicationInfo)} would, so there is no need to call that method
-     * directly.
-     *
-     * @param changeId The ID of the compatibility change in question.
-     * @param appInfo Representing the app in question.
-     * @return {@code true} if the change is enabled for the current app.
-     */
-    public static boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
+    @Override
+    public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
         if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) {
             reportChange(changeId, appInfo);
             return true;
         }
         return false;
     }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
+        CompatConfig.get().dumpConfig(pw);
+    }
 }
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 7be1b8a..c46fc20 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -29,6 +29,7 @@
 import android.opengl.GLES20;
 import android.os.IBinder;
 import android.util.Slog;
+import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
@@ -72,6 +73,9 @@
     // See code for details.
     private static final int DEJANK_FRAMES = 3;
 
+    private static final int EGL_GL_COLORSPACE_KHR = 0x309D;
+    private static final int EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490;
+
     private final int mDisplayId;
 
     // Set to true when the animation context has been fully prepared.
@@ -93,6 +97,7 @@
     private EGLSurface mEglSurface;
     private boolean mSurfaceVisible;
     private float mSurfaceAlpha;
+    private boolean mIsWideColor;
 
     // Texture names.  We only use one texture, which contains the screenshot.
     private final int[] mTexNames = new int[1];
@@ -482,6 +487,8 @@
                     return false;
                 }
 
+                mIsWideColor = SurfaceControl.getActiveColorMode(token)
+                        == Display.COLOR_MODE_DISPLAY_P3;
                 SurfaceControl.screenshot(token, s);
                 st.updateTexImage();
                 st.getTransformMatrix(mTexMatrix);
@@ -608,8 +615,16 @@
     private boolean createEglSurface() {
         if (mEglSurface == null) {
             int[] eglSurfaceAttribList = new int[] {
+                    EGL14.EGL_NONE,
+                    EGL14.EGL_NONE,
                     EGL14.EGL_NONE
             };
+
+            // If the current display is in wide color, then so is the screenshot.
+            if (mIsWideColor) {
+                eglSurfaceAttribList[0] = EGL_GL_COLORSPACE_KHR;
+                eglSurfaceAttribList[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
+            }
             // turn our SurfaceControl into a Surface
             mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface,
                     eglSurfaceAttribList, 0);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index 2c0cacd..52cede2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -290,8 +290,7 @@
             new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_UP, CEC_KEYCODE_CHANNEL_UP),
             new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_DOWN, CEC_KEYCODE_CHANNEL_DOWN),
             new KeycodeEntry(KeyEvent.KEYCODE_LAST_CHANNEL, CEC_KEYCODE_PREVIOUS_CHANNEL),
-            // No Android keycode defined for CEC_KEYCODE_SOUND_SELECT
-            new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SOUND_SELECT),
+            new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK, CEC_KEYCODE_SOUND_SELECT),
             new KeycodeEntry(KeyEvent.KEYCODE_TV_INPUT, CEC_KEYCODE_INPUT_SELECT),
             new KeycodeEntry(KeyEvent.KEYCODE_INFO, CEC_KEYCODE_DISPLAY_INFORMATION),
             // No Android keycode defined for CEC_KEYCODE_HELP
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 6911b7c..461f19b 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1608,13 +1608,11 @@
         if (DEBUG) Log.d(TAG, "reportGnssServiceDied");
         mHandler.post(() -> {
             setupNativeGnssService(/* reinitializeGnssServiceHandle = */ true);
+            // resend configuration into the restarted HAL service.
+            reloadGpsProperties();
             if (isGpsEnabled()) {
                 setGpsEnabled(false);
-
                 updateEnabled();
-
-                // resend configuration into the restarted HAL service.
-                reloadGpsProperties();
             }
         });
     }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 433ce81..9510db0 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -446,7 +446,7 @@
 
         public boolean hasEnrolledBiometrics(int userId) {
             BiometricManager bm = mContext.getSystemService(BiometricManager.class);
-            return bm.canAuthenticate(userId) == BiometricManager.BIOMETRIC_SUCCESS;
+            return bm.hasEnrolledBiometrics(userId);
         }
 
         public int binderGetCallingUid() {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index f0e431e..84ae7c7 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -48,6 +48,8 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -390,6 +392,17 @@
         return stored;
     }
 
+    private void fsyncDirectory(File directory) {
+        try {
+            try (FileChannel file = FileChannel.open(directory.toPath(),
+                    StandardOpenOption.READ)) {
+                file.force(true);
+            }
+        } catch (IOException e) {
+            Slog.e(TAG, "Error syncing directory: " + directory, e);
+        }
+    }
+
     private void writeFile(String name, byte[] hash) {
         synchronized (mFileWriteLock) {
             RandomAccessFile raf = null;
@@ -406,6 +419,7 @@
                     raf.write(hash, 0, hash.length);
                 }
                 raf.close();
+                fsyncDirectory((new File(name)).getAbsoluteFile().getParentFile());
             } catch (IOException e) {
                 Slog.e(TAG, "Error writing to file " + e);
             } finally {
diff --git a/services/core/java/com/android/server/locksettings/TEST_MAPPING b/services/core/java/com/android/server/locksettings/TEST_MAPPING
new file mode 100644
index 0000000..c1cba5f
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+    "presubmit": [
+        {
+            "name": "CtsDevicePolicyManagerTestCases",
+            "options": [
+                {
+                    "include-annotation": "com.android.cts.devicepolicy.annotations.LockSettingsTest"
+                },
+                {
+                    "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+                }
+            ]
+        }
+    ]
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index c54bfc0..0ad6c2a 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -19,6 +19,7 @@
 import android.app.KeyguardManager;
 import android.content.Context;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.security.GateKeeper;
 import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyPermanentlyInvalidatedException;
@@ -437,25 +438,31 @@
         // so it may live in memory for some time.
         SecretKey secretKey = generateAesKey();
 
-        long secureUserId = getGateKeeperService().getSecureUserId(userId);
-        // TODO(b/124095438): Propagate this failure instead of silently failing.
-        if (secureUserId == GateKeeper.INVALID_SECURE_USER_ID) {
-            Log.e(TAG, "No SID available for user " + userId);
-            return;
-        }
-
-        // Store decryption key first since it is more likely to fail.
-        mKeyStore.setEntry(
-                decryptAlias,
-                new KeyStore.SecretKeyEntry(secretKey),
+        KeyProtection.Builder decryptionKeyProtection =
                 new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
                     .setUserAuthenticationRequired(true)
                     .setUserAuthenticationValidityDurationSeconds(
                             USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS)
                     .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
-                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE);
+        if (userId != UserHandle.USER_SYSTEM) {
+            // Bind decryption key to secondary profile lock screen secret.
+            long secureUserId = getGateKeeperService().getSecureUserId(userId);
+            // TODO(b/124095438): Propagate this failure instead of silently failing.
+            if (secureUserId == GateKeeper.INVALID_SECURE_USER_ID) {
+                Log.e(TAG, "No SID available for user " + userId);
+                return;
+            }
+            decryptionKeyProtection
                     .setBoundToSpecificSecureUserId(secureUserId)
-                    .build());
+                    // Ignore caller uid which always belongs to the primary profile.
+                    .setCriticalToDeviceEncryption(true);
+        }
+        // Store decryption key first since it is more likely to fail.
+        mKeyStore.setEntry(
+                decryptAlias,
+                new KeyStore.SecretKeyEntry(secretKey),
+                decryptionKeyProtection.build());
         mKeyStore.setEntry(
                 encryptAlias,
                 new KeyStore.SecretKeyEntry(secretKey),
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 4a6eb27..4828bbf 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -196,18 +196,20 @@
 
     public void dump(PrintWriter pw, DumpFilter filter) {
         pw.println("    Allowed " + getCaption() + "s:");
-        final int N = mApproved.size();
-        for (int i = 0 ; i < N; i++) {
-            final int userId = mApproved.keyAt(i);
-            final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
-            if (approvedByType != null) {
-                final int M = approvedByType.size();
-                for (int j = 0; j < M; j++) {
-                    final boolean isPrimary = approvedByType.keyAt(j);
-                    final ArraySet<String> approved = approvedByType.valueAt(j);
-                    if (approvedByType != null && approvedByType.size() > 0) {
-                        pw.println("      " + String.join(ENABLED_SERVICES_SEPARATOR, approved)
-                                + " (user: " + userId + " isPrimary: " + isPrimary + ")");
+        synchronized (mApproved) {
+            final int N = mApproved.size();
+            for (int i = 0; i < N; i++) {
+                final int userId = mApproved.keyAt(i);
+                final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
+                if (approvedByType != null) {
+                    final int M = approvedByType.size();
+                    for (int j = 0; j < M; j++) {
+                        final boolean isPrimary = approvedByType.keyAt(j);
+                        final ArraySet<String> approved = approvedByType.valueAt(j);
+                        if (approvedByType != null && approvedByType.size() > 0) {
+                            pw.println("      " + String.join(ENABLED_SERVICES_SEPARATOR, approved)
+                                    + " (user: " + userId + " isPrimary: " + isPrimary + ")");
+                        }
                     }
                 }
             }
@@ -240,23 +242,25 @@
 
     public void dump(ProtoOutputStream proto, DumpFilter filter) {
         proto.write(ManagedServicesProto.CAPTION, getCaption());
-        final int N = mApproved.size();
-        for (int i = 0 ; i < N; i++) {
-            final int userId = mApproved.keyAt(i);
-            final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
-            if (approvedByType != null) {
-                final int M = approvedByType.size();
-                for (int j = 0; j < M; j++) {
-                    final boolean isPrimary = approvedByType.keyAt(j);
-                    final ArraySet<String> approved = approvedByType.valueAt(j);
-                    if (approvedByType != null && approvedByType.size() > 0) {
-                        final long sToken = proto.start(ManagedServicesProto.APPROVED);
-                        for (String s : approved) {
-                            proto.write(ServiceProto.NAME, s);
+        synchronized (mApproved) {
+            final int N = mApproved.size();
+            for (int i = 0; i < N; i++) {
+                final int userId = mApproved.keyAt(i);
+                final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
+                if (approvedByType != null) {
+                    final int M = approvedByType.size();
+                    for (int j = 0; j < M; j++) {
+                        final boolean isPrimary = approvedByType.keyAt(j);
+                        final ArraySet<String> approved = approvedByType.valueAt(j);
+                        if (approvedByType != null && approvedByType.size() > 0) {
+                            final long sToken = proto.start(ManagedServicesProto.APPROVED);
+                            for (String s : approved) {
+                                proto.write(ServiceProto.NAME, s);
+                            }
+                            proto.write(ServiceProto.USER_ID, userId);
+                            proto.write(ServiceProto.IS_PRIMARY, isPrimary);
+                            proto.end(sToken);
                         }
-                        proto.write(ServiceProto.USER_ID, userId);
-                        proto.write(ServiceProto.IS_PRIMARY, isPrimary);
-                        proto.end(sToken);
                     }
                 }
             }
@@ -315,33 +319,36 @@
             trimApprovedListsAccordingToInstalledServices(userId);
         }
 
-        final int N = mApproved.size();
-        for (int i = 0 ; i < N; i++) {
-            final int approvedUserId = mApproved.keyAt(i);
-            if (forBackup && approvedUserId != userId) {
-                continue;
-            }
-            final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
-            if (approvedByType != null) {
-                final int M = approvedByType.size();
-                for (int j = 0; j < M; j++) {
-                    final boolean isPrimary = approvedByType.keyAt(j);
-                    final Set<String> approved = approvedByType.valueAt(j);
-                    if (approved != null) {
-                        String allowedItems = String.join(ENABLED_SERVICES_SEPARATOR, approved);
-                        out.startTag(null, TAG_MANAGED_SERVICES);
-                        out.attribute(null, ATT_APPROVED_LIST, allowedItems);
-                        out.attribute(null, ATT_USER_ID, Integer.toString(approvedUserId));
-                        out.attribute(null, ATT_IS_PRIMARY, Boolean.toString(isPrimary));
-                        writeExtraAttributes(out, approvedUserId);
-                        out.endTag(null, TAG_MANAGED_SERVICES);
+        synchronized (mApproved) {
+            final int N = mApproved.size();
+            for (int i = 0; i < N; i++) {
+                final int approvedUserId = mApproved.keyAt(i);
+                if (forBackup && approvedUserId != userId) {
+                    continue;
+                }
+                final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
+                if (approvedByType != null) {
+                    final int M = approvedByType.size();
+                    for (int j = 0; j < M; j++) {
+                        final boolean isPrimary = approvedByType.keyAt(j);
+                        final Set<String> approved = approvedByType.valueAt(j);
+                        if (approved != null) {
+                            String allowedItems = String.join(ENABLED_SERVICES_SEPARATOR, approved);
+                            out.startTag(null, TAG_MANAGED_SERVICES);
+                            out.attribute(null, ATT_APPROVED_LIST, allowedItems);
+                            out.attribute(null, ATT_USER_ID, Integer.toString(approvedUserId));
+                            out.attribute(null, ATT_IS_PRIMARY, Boolean.toString(isPrimary));
+                            writeExtraAttributes(out, approvedUserId);
+                            out.endTag(null, TAG_MANAGED_SERVICES);
 
-                        if (!forBackup && isPrimary) {
-                            // Also write values to settings, for observers who haven't migrated yet
-                            Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                                    getConfig().secureSettingName, allowedItems, approvedUserId);
+                            if (!forBackup && isPrimary) {
+                                // Also write values to settings, for observers who haven't migrated yet
+                                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                                        getConfig().secureSettingName, allowedItems,
+                                        approvedUserId);
+                            }
+
                         }
-
                     }
                 }
             }
@@ -440,23 +447,25 @@
         if (TextUtils.isEmpty(approved)) {
             approved = "";
         }
-        ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
-        if (approvedByType == null) {
-            approvedByType = new ArrayMap<>();
-            mApproved.put(userId, approvedByType);
-        }
+        synchronized (mApproved) {
+            ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
+            if (approvedByType == null) {
+                approvedByType = new ArrayMap<>();
+                mApproved.put(userId, approvedByType);
+            }
 
-        ArraySet<String> approvedList = approvedByType.get(isPrimary);
-        if (approvedList == null) {
-            approvedList = new ArraySet<>();
-            approvedByType.put(isPrimary, approvedList);
-        }
+            ArraySet<String> approvedList = approvedByType.get(isPrimary);
+            if (approvedList == null) {
+                approvedList = new ArraySet<>();
+                approvedByType.put(isPrimary, approvedList);
+            }
 
-        String[] approvedArray = approved.split(ENABLED_SERVICES_SEPARATOR);
-        for (String pkgOrComponent : approvedArray) {
-            String approvedItem = getApprovedValue(pkgOrComponent);
-            if (approvedItem != null) {
-                approvedList.add(approvedItem);
+            String[] approvedArray = approved.split(ENABLED_SERVICES_SEPARATOR);
+            for (String pkgOrComponent : approvedArray) {
+                String approvedItem = getApprovedValue(pkgOrComponent);
+                if (approvedItem != null) {
+                    approvedList.add(approvedItem);
+                }
             }
         }
     }
@@ -469,23 +478,25 @@
             boolean isPrimary, boolean enabled) {
         Slog.i(TAG,
                 (enabled ? " Allowing " : "Disallowing ") + mConfig.caption + " " + pkgOrComponent);
-        ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.get(userId);
-        if (allowedByType == null) {
-            allowedByType = new ArrayMap<>();
-            mApproved.put(userId, allowedByType);
-        }
-        ArraySet<String> approved = allowedByType.get(isPrimary);
-        if (approved == null) {
-            approved = new ArraySet<>();
-            allowedByType.put(isPrimary, approved);
-        }
-        String approvedItem = getApprovedValue(pkgOrComponent);
+        synchronized (mApproved) {
+            ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.get(userId);
+            if (allowedByType == null) {
+                allowedByType = new ArrayMap<>();
+                mApproved.put(userId, allowedByType);
+            }
+            ArraySet<String> approved = allowedByType.get(isPrimary);
+            if (approved == null) {
+                approved = new ArraySet<>();
+                allowedByType.put(isPrimary, approved);
+            }
+            String approvedItem = getApprovedValue(pkgOrComponent);
 
-        if (approvedItem != null) {
-            if (enabled) {
-                approved.add(approvedItem);
-            } else {
-                approved.remove(approvedItem);
+            if (approvedItem != null) {
+                if (enabled) {
+                    approved.add(approvedItem);
+                } else {
+                    approved.remove(approvedItem);
+                }
             }
         }
 
@@ -504,22 +515,26 @@
     }
 
     protected String getApproved(int userId, boolean primary) {
-        final ArrayMap<Boolean, ArraySet<String>> allowedByType =
-                mApproved.getOrDefault(userId, new ArrayMap<>());
-        ArraySet<String> approved = allowedByType.getOrDefault(primary, new ArraySet<>());
-        return String.join(ENABLED_SERVICES_SEPARATOR, approved);
+        synchronized (mApproved) {
+            final ArrayMap<Boolean, ArraySet<String>> allowedByType =
+                    mApproved.getOrDefault(userId, new ArrayMap<>());
+            ArraySet<String> approved = allowedByType.getOrDefault(primary, new ArraySet<>());
+            return String.join(ENABLED_SERVICES_SEPARATOR, approved);
+        }
     }
 
     protected List<ComponentName> getAllowedComponents(int userId) {
         final List<ComponentName> allowedComponents = new ArrayList<>();
-        final ArrayMap<Boolean, ArraySet<String>> allowedByType =
-                mApproved.getOrDefault(userId, new ArrayMap<>());
-        for (int i = 0; i < allowedByType.size(); i++) {
-            final ArraySet<String> allowed = allowedByType.valueAt(i);
-            for (int j = 0; j < allowed.size(); j++) {
-                ComponentName cn = ComponentName.unflattenFromString(allowed.valueAt(j));
-                if (cn != null) {
-                    allowedComponents.add(cn);
+        synchronized (mApproved) {
+            final ArrayMap<Boolean, ArraySet<String>> allowedByType =
+                    mApproved.getOrDefault(userId, new ArrayMap<>());
+            for (int i = 0; i < allowedByType.size(); i++) {
+                final ArraySet<String> allowed = allowedByType.valueAt(i);
+                for (int j = 0; j < allowed.size(); j++) {
+                    ComponentName cn = ComponentName.unflattenFromString(allowed.valueAt(j));
+                    if (cn != null) {
+                        allowedComponents.add(cn);
+                    }
                 }
             }
         }
@@ -528,14 +543,16 @@
 
     protected List<String> getAllowedPackages(int userId) {
         final List<String> allowedPackages = new ArrayList<>();
-        final ArrayMap<Boolean, ArraySet<String>> allowedByType =
-                mApproved.getOrDefault(userId, new ArrayMap<>());
-        for (int i = 0; i < allowedByType.size(); i++) {
-            final ArraySet<String> allowed = allowedByType.valueAt(i);
-            for (int j = 0; j < allowed.size(); j++) {
-                String pkgName = getPackageName(allowed.valueAt(j));
-                if (!TextUtils.isEmpty(pkgName)) {
-                    allowedPackages.add(pkgName);
+        synchronized (mApproved) {
+            final ArrayMap<Boolean, ArraySet<String>> allowedByType =
+                    mApproved.getOrDefault(userId, new ArrayMap<>());
+            for (int i = 0; i < allowedByType.size(); i++) {
+                final ArraySet<String> allowed = allowedByType.valueAt(i);
+                for (int j = 0; j < allowed.size(); j++) {
+                    String pkgName = getPackageName(allowed.valueAt(j));
+                    if (!TextUtils.isEmpty(pkgName)) {
+                        allowedPackages.add(pkgName);
+                    }
                 }
             }
         }
@@ -543,12 +560,14 @@
     }
 
     protected boolean isPackageOrComponentAllowed(String pkgOrComponent, int userId) {
-        ArrayMap<Boolean, ArraySet<String>> allowedByType =
-                mApproved.getOrDefault(userId, new ArrayMap<>());
-        for (int i = 0; i < allowedByType.size(); i++) {
-            ArraySet<String> allowed = allowedByType.valueAt(i);
-            if (allowed.contains(pkgOrComponent)) {
-                return true;
+        synchronized (mApproved) {
+            ArrayMap<Boolean, ArraySet<String>> allowedByType =
+                    mApproved.getOrDefault(userId, new ArrayMap<>());
+            for (int i = 0; i < allowedByType.size(); i++) {
+                ArraySet<String> allowed = allowedByType.valueAt(i);
+                if (allowed.contains(pkgOrComponent)) {
+                    return true;
+                }
             }
         }
         return false;
@@ -558,19 +577,21 @@
         if (pkg == null) {
             return false;
         }
-        ArrayMap<Boolean, ArraySet<String>> allowedByType =
-                mApproved.getOrDefault(userId, new ArrayMap<>());
-        for (int i = 0; i < allowedByType.size(); i++) {
-            ArraySet<String> allowed = allowedByType.valueAt(i);
-            for (String allowedEntry : allowed) {
-                ComponentName component = ComponentName.unflattenFromString(allowedEntry);
-                if (component != null) {
-                    if (pkg.equals(component.getPackageName())) {
-                        return true;
-                    }
-                } else {
-                    if (pkg.equals(allowedEntry)) {
-                        return true;
+        synchronized (mApproved) {
+            ArrayMap<Boolean, ArraySet<String>> allowedByType =
+                    mApproved.getOrDefault(userId, new ArrayMap<>());
+            for (int i = 0; i < allowedByType.size(); i++) {
+                ArraySet<String> allowed = allowedByType.valueAt(i);
+                for (String allowedEntry : allowed) {
+                    ComponentName component = ComponentName.unflattenFromString(allowedEntry);
+                    if (component != null) {
+                        if (pkg.equals(component.getPackageName())) {
+                            return true;
+                        }
+                    } else {
+                        if (pkg.equals(allowedEntry)) {
+                            return true;
+                        }
                     }
                 }
             }
@@ -616,7 +637,9 @@
 
     public void onUserRemoved(int user) {
         Slog.i(TAG, "Removing approved services for removed user " + user);
-        mApproved.remove(user);
+        synchronized (mApproved) {
+            mApproved.remove(user);
+        }
         rebindServices(true, user);
     }
 
@@ -797,14 +820,16 @@
 
     protected Set<String> getAllowedPackages() {
         final Set<String> allowedPackages = new ArraySet<>();
-        for (int k = 0; k < mApproved.size(); k++) {
-            ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.valueAt(k);
-            for (int i = 0; i < allowedByType.size(); i++) {
-                final ArraySet<String> allowed = allowedByType.valueAt(i);
-                for (int j = 0; j < allowed.size(); j++) {
-                    String pkgName = getPackageName(allowed.valueAt(j));
-                    if (!TextUtils.isEmpty(pkgName)) {
-                        allowedPackages.add(pkgName);
+        synchronized (mApproved) {
+            for (int k = 0; k < mApproved.size(); k++) {
+                ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.valueAt(k);
+                for (int i = 0; i < allowedByType.size(); i++) {
+                    final ArraySet<String> allowed = allowedByType.valueAt(i);
+                    for (int j = 0; j < allowed.size(); j++) {
+                        String pkgName = getPackageName(allowed.valueAt(j));
+                        if (!TextUtils.isEmpty(pkgName)) {
+                            allowedPackages.add(pkgName);
+                        }
                     }
                 }
             }
@@ -813,22 +838,24 @@
     }
 
     private void trimApprovedListsAccordingToInstalledServices(int userId) {
-        final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
-        if (approvedByType == null) {
-            return;
-        }
-        for (int i = 0; i < approvedByType.size(); i++) {
-            final ArraySet<String> approved = approvedByType.valueAt(i);
-            for (int j = approved.size() - 1; j >= 0; j--) {
-                final String approvedPackageOrComponent = approved.valueAt(j);
-                if (!isValidEntry(approvedPackageOrComponent, userId)){
-                    approved.removeAt(j);
-                    Slog.v(TAG, "Removing " + approvedPackageOrComponent
-                            + " from approved list; no matching services found");
-                } else {
-                    if (DEBUG) {
-                        Slog.v(TAG, "Keeping " + approvedPackageOrComponent
-                                + " on approved list; matching services found");
+        synchronized (mApproved) {
+            final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
+            if (approvedByType == null) {
+                return;
+            }
+            for (int i = 0; i < approvedByType.size(); i++) {
+                final ArraySet<String> approved = approvedByType.valueAt(i);
+                for (int j = approved.size() - 1; j >= 0; j--) {
+                    final String approvedPackageOrComponent = approved.valueAt(j);
+                    if (!isValidEntry(approvedPackageOrComponent, userId)) {
+                        approved.removeAt(j);
+                        Slog.v(TAG, "Removing " + approvedPackageOrComponent
+                                + " from approved list; no matching services found");
+                    } else {
+                        if (DEBUG) {
+                            Slog.v(TAG, "Keeping " + approvedPackageOrComponent
+                                    + " on approved list; matching services found");
+                        }
                     }
                 }
             }
@@ -837,20 +864,23 @@
 
     private boolean removeUninstalledItemsFromApprovedLists(int uninstalledUserId, String pkg) {
         boolean removed = false;
-        final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(uninstalledUserId);
-        if (approvedByType != null) {
-            int M = approvedByType.size();
-            for (int j = 0; j < M; j++) {
-                final ArraySet<String> approved = approvedByType.valueAt(j);
-                int O = approved.size();
-                for (int k = O - 1; k >= 0; k--) {
-                    final String packageOrComponent = approved.valueAt(k);
-                    final String packageName = getPackageName(packageOrComponent);
-                    if (TextUtils.equals(pkg, packageName)) {
-                        approved.removeAt(k);
-                        if (DEBUG) {
-                            Slog.v(TAG, "Removing " + packageOrComponent
-                                    + " from approved list; uninstalled");
+        synchronized (mApproved) {
+            final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(
+                    uninstalledUserId);
+            if (approvedByType != null) {
+                int M = approvedByType.size();
+                for (int j = 0; j < M; j++) {
+                    final ArraySet<String> approved = approvedByType.valueAt(j);
+                    int O = approved.size();
+                    for (int k = O - 1; k >= 0; k--) {
+                        final String packageOrComponent = approved.valueAt(k);
+                        final String packageName = getPackageName(packageOrComponent);
+                        if (TextUtils.equals(pkg, packageName)) {
+                            approved.removeAt(k);
+                            if (DEBUG) {
+                                Slog.v(TAG, "Removing " + packageOrComponent
+                                        + " from approved list; uninstalled");
+                            }
                         }
                     }
                 }
@@ -887,17 +917,19 @@
 
         for (int i = 0; i < nUserIds; ++i) {
             final int userId = userIds.get(i);
-            final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userId);
-            if (approvedLists != null) {
-                final int N = approvedLists.size();
-                for (int j = 0; j < N; j++) {
-                    ArraySet<ComponentName> approvedByUser = componentsByUser.get(userId);
-                    if (approvedByUser == null) {
-                        approvedByUser = new ArraySet<>();
-                        componentsByUser.put(userId, approvedByUser);
+            synchronized (mApproved) {
+                final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userId);
+                if (approvedLists != null) {
+                    final int N = approvedLists.size();
+                    for (int j = 0; j < N; j++) {
+                        ArraySet<ComponentName> approvedByUser = componentsByUser.get(userId);
+                        if (approvedByUser == null) {
+                            approvedByUser = new ArraySet<>();
+                            componentsByUser.put(userId, approvedByUser);
+                        }
+                        approvedByUser.addAll(
+                                loadComponentNamesFromValues(approvedLists.valueAt(j), userId));
                     }
-                    approvedByUser.addAll(
-                            loadComponentNamesFromValues(approvedLists.valueAt(j), userId));
                 }
             }
         }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d30895e..217c0bd 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -473,6 +473,8 @@
     private MetricsLogger mMetricsLogger;
     private TriPredicate<String, Integer, String> mAllowedManagedServicePackages;
 
+    private final SavePolicyFileRunnable mSavePolicyFile = new SavePolicyFileRunnable();
+
     private static class Archive {
         final int mBufferSize;
         final ArrayDeque<StatusBarNotification> mBuffer;
@@ -666,7 +668,14 @@
 
     @VisibleForTesting
     protected void handleSavePolicyFile() {
-        IoThread.getHandler().post(() -> {
+        if (!IoThread.getHandler().hasCallbacks(mSavePolicyFile)) {
+            IoThread.getHandler().post(mSavePolicyFile);
+        }
+    }
+
+    private final class SavePolicyFileRunnable implements Runnable {
+        @Override
+        public void run() {
             if (DBG) Slog.d(TAG, "handleSavePolicyFile");
             synchronized (mPolicyFile) {
                 final FileOutputStream stream;
@@ -686,7 +695,7 @@
                 }
             }
             BackupManager.dataChanged(getContext().getPackageName());
-        });
+        }
     }
 
     private void writePolicyXml(OutputStream stream, boolean forBackup, int userId)
@@ -1845,6 +1854,7 @@
                     }
                     if (properties.getKeyset()
                             .contains(SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE)) {
+                        mAssistants.allowAdjustmentType(Adjustment.KEY_IMPORTANCE);
                         mAssistants.resetDefaultAssistantsIfNecessary();
                     }
                 });
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 6f28675..934511b 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -296,22 +296,12 @@
      */
     private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName,
             final int userId, final int flags) {
-        final List<OverlayInfo> ois = new ArrayList<>();
+        final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName,
+                userId);
 
-        // Framework overlays added first because order matters when resolving a resource
-        if (!"android".equals(targetPackageName)) {
-            ois.addAll(mSettings.getOverlaysForTarget("android", userId));
-        }
-
-        // Then add the targeted, non-framework overlays which have higher priority
-        ois.addAll(mSettings.getOverlaysForTarget(targetPackageName, userId));
-
-        final List<String> enabledBaseCodePaths = new ArrayList<>(ois.size());
-
+        // Update the state for any overlay that targets this package.
         boolean modified = false;
-        final int n = ois.size();
-        for (int i = 0; i < n; i++) {
-            final OverlayInfo oi = ois.get(i);
+        for (final OverlayInfo oi : targetOverlays) {
             final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName,
                     userId);
             if (overlayPackage == null) {
@@ -324,25 +314,39 @@
                     Slog.e(TAG, "failed to update settings", e);
                     modified |= mSettings.remove(oi.packageName, userId);
                 }
-
-                if (oi.isEnabled() && overlayPackage.applicationInfo != null) {
-                    enabledBaseCodePaths.add(overlayPackage.applicationInfo.getBaseCodePath());
-                }
             }
         }
 
         if (!modified) {
+            // Update the overlay paths of the target within package manager if necessary.
+            final List<String> enabledOverlayPaths = new ArrayList<>(targetOverlays.size());
+
+            // Framework overlays are first in the overlay paths of a package within PackageManager.
+            for (final OverlayInfo oi : mSettings.getOverlaysForTarget("android", userId)) {
+                if (oi.isEnabled()) {
+                    enabledOverlayPaths.add(oi.baseCodePath);
+                }
+            }
+
+            for (final OverlayInfo oi : targetOverlays) {
+                if (oi.isEnabled()) {
+                    enabledOverlayPaths.add(oi.baseCodePath);
+                }
+            }
+
+            // TODO(): Use getEnabledOverlayPaths(userId, targetPackageName) instead of
+            // resourceDirs if in the future resourceDirs contains APKs other than overlays
             PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, userId);
             ApplicationInfo appInfo = packageInfo == null ? null : packageInfo.applicationInfo;
             String[] resourceDirs = appInfo == null ? null : appInfo.resourceDirs;
 
             // If the lists aren't the same length, the enabled overlays have changed
-            if (ArrayUtils.size(resourceDirs) != enabledBaseCodePaths.size()) {
+            if (ArrayUtils.size(resourceDirs) != enabledOverlayPaths.size()) {
                 modified = true;
             } else if (resourceDirs != null) {
                 // If any element isn't equal, an overlay or the order of overlays has changed
                 for (int index = 0; index < resourceDirs.length; index++) {
-                    if (!resourceDirs[index].equals(enabledBaseCodePaths.get(index))) {
+                    if (!resourceDirs[index].equals(enabledOverlayPaths.get(index))) {
                         modified = true;
                         break;
                     }
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
new file mode 100644
index 0000000..ab8cc53
--- /dev/null
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -0,0 +1,372 @@
+/*
+ * 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.pm;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.os.Build;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.permission.IPermissionManager;
+import android.provider.DeviceConfig;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The entity responsible for filtering visibility between apps based on declarations in their
+ * manifests.
+ */
+class AppsFilter {
+
+    private static final String TAG = PackageManagerService.TAG;
+    /**
+     * This contains a list of packages that are implicitly queryable because another app explicitly
+     * interacted with it. For example, if application A starts a service in application B,
+     * application B is implicitly allowed to query for application A; regardless of any manifest
+     * entries.
+     */
+    private final SparseArray<HashMap<String, ArrayList<String>>> mImplicitlyQueryable =
+            new SparseArray<>();
+
+    /**
+     * A mapping from the set of packages that query other packages via package name to the
+     * list of packages that they can see.
+     */
+    private final HashMap<String, List<String>> mQueriesViaPackage = new HashMap<>();
+
+    /**
+     * A mapping from the set of packages that query others via intent to the list
+     * of packages that the intents resolve to.
+     */
+    private final HashMap<String, List<String>> mQueriesViaIntent = new HashMap<>();
+
+    /**
+     * A set of packages that are always queryable by any package, regardless of their manifest
+     * content.
+     */
+    private final HashSet<String> mForceQueryable;
+    /**
+     * A set of packages that are always queryable by any package, regardless of their manifest
+     * content.
+     */
+    private final Set<String> mForceQueryableByDevice;
+
+    /** True if all system apps should be made queryable by default. */
+    private final boolean mSystemAppsQueryable;
+
+    private final IPermissionManager mPermissionManager;
+
+    private final AppOpsManager mAppOpsManager;
+    private final ConfigProvider mConfigProvider;
+
+    AppsFilter(ConfigProvider configProvider, IPermissionManager permissionManager,
+            AppOpsManager appOpsManager, String[] forceQueryableWhitelist,
+            boolean systemAppsQueryable) {
+        mConfigProvider = configProvider;
+        mAppOpsManager = appOpsManager;
+        final HashSet<String> forceQueryableByDeviceSet = new HashSet<>();
+        Collections.addAll(forceQueryableByDeviceSet, forceQueryableWhitelist);
+        this.mForceQueryableByDevice = Collections.unmodifiableSet(forceQueryableByDeviceSet);
+        this.mForceQueryable = new HashSet<>();
+        mPermissionManager = permissionManager;
+        mSystemAppsQueryable = systemAppsQueryable;
+    }
+
+    public static AppsFilter create(Context context) {
+        final boolean forceSystemAppsQueryable =
+                context.getResources().getBoolean(R.bool.config_forceSystemPackagesQueryable);
+        final ConfigProvider configProvider = () -> DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
+                "package_query_filtering_enabled",
+                false);
+        final String[] forcedQueryablePackageNames;
+        if (forceSystemAppsQueryable) {
+            // all system apps already queryable, no need to read and parse individual exceptions
+            forcedQueryablePackageNames = new String[]{};
+        } else {
+            forcedQueryablePackageNames =
+                    context.getResources().getStringArray(R.array.config_forceQueryablePackages);
+            for (int i = 0; i < forcedQueryablePackageNames.length; i++) {
+                forcedQueryablePackageNames[i] = forcedQueryablePackageNames[i].intern();
+            }
+        }
+        IPermissionManager permissionmgr =
+                (IPermissionManager) ServiceManager.getService("permissionmgr");
+        return new AppsFilter(configProvider, permissionmgr,
+                context.getSystemService(AppOpsManager.class), forcedQueryablePackageNames,
+                forceSystemAppsQueryable);
+    }
+
+    /** Returns true if the querying package may query for the potential target package */
+    private static boolean canQuery(PackageParser.Package querying,
+            PackageParser.Package potentialTarget) {
+        if (querying.mQueriesIntents == null) {
+            return false;
+        }
+        for (Intent intent : querying.mQueriesIntents) {
+            for (PackageParser.Activity activity : potentialTarget.activities) {
+                if (activity.intents != null) {
+                    for (PackageParser.ActivityIntentInfo filter : activity.intents) {
+                        if (matches(intent, filter)) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /** Returns true if the given intent matches the given filter. */
+    private static boolean matches(Intent intent, PackageParser.ActivityIntentInfo filter) {
+        return filter.match(intent.getAction(), intent.getType(), intent.getScheme(),
+                intent.getData(), intent.getCategories(), "AppsFilter") > 0;
+    }
+
+    /**
+     * Marks that a package initiated an interaction with another package, granting visibility of
+     * the prior from the former.
+     *
+     * @param initiatingPackage the package initiating the interaction
+     * @param targetPackage     the package being interacted with and thus gaining visibility of the
+     *                          initiating package.
+     * @param userId            the user in which this interaction was taking place
+     */
+    private void markAppInteraction(
+            PackageSetting initiatingPackage, PackageSetting targetPackage, int userId) {
+        HashMap<String, ArrayList<String>> currentUser = mImplicitlyQueryable.get(userId);
+        if (currentUser == null) {
+            currentUser = new HashMap<>();
+            mImplicitlyQueryable.put(userId, currentUser);
+        }
+        if (!currentUser.containsKey(targetPackage.pkg.packageName)) {
+            currentUser.put(targetPackage.pkg.packageName, new ArrayList<>());
+        }
+        currentUser.get(targetPackage.pkg.packageName).add(initiatingPackage.pkg.packageName);
+    }
+
+    /**
+     * Adds a package that should be considered when filtering visibility between apps.
+     *
+     * @param newPkg   the new package being added
+     * @param existing all other packages currently on the device.
+     */
+    public void addPackage(PackageParser.Package newPkg,
+            Map<String, PackageParser.Package> existing) {
+        // let's re-evaluate the ability of already added packages to see this new package
+        if (newPkg.mForceQueryable
+                || (mSystemAppsQueryable && (newPkg.isSystem() || newPkg.isUpdatedSystemApp()))) {
+            mForceQueryable.add(newPkg.packageName);
+        } else {
+            for (String packageName : mQueriesViaIntent.keySet()) {
+                if (packageName == newPkg.packageName) {
+                    continue;
+                }
+                final PackageParser.Package existingPackage = existing.get(packageName);
+                if (canQuery(existingPackage, newPkg)) {
+                    mQueriesViaIntent.get(packageName).add(newPkg.packageName);
+                }
+            }
+        }
+        // if the new package declares them, let's evaluate its ability to see existing packages
+        mQueriesViaIntent.put(newPkg.packageName, new ArrayList<>());
+        for (PackageParser.Package existingPackage : existing.values()) {
+            if (existingPackage.packageName == newPkg.packageName) {
+                continue;
+            }
+            if (existingPackage.mForceQueryable
+                    || (mSystemAppsQueryable
+                    && (newPkg.isSystem() || newPkg.isUpdatedSystemApp()))) {
+                continue;
+            }
+            if (canQuery(newPkg, existingPackage)) {
+                mQueriesViaIntent.get(newPkg.packageName).add(existingPackage.packageName);
+            }
+        }
+        final ArrayList<String> queriesPackages = new ArrayList<>(
+                newPkg.mQueriesPackages == null ? 0 : newPkg.mQueriesPackages.size());
+        if (newPkg.mQueriesPackages != null) {
+            queriesPackages.addAll(newPkg.mQueriesPackages);
+        }
+        mQueriesViaPackage.put(newPkg.packageName, queriesPackages);
+    }
+
+    /**
+     * Removes a package for consideration when filtering visibility between apps.
+     *
+     * @param packageName the name of the package being removed.
+     */
+    public void removePackage(String packageName) {
+        mForceQueryable.remove(packageName);
+
+        for (int i = 0; i < mImplicitlyQueryable.size(); i++) {
+            mImplicitlyQueryable.valueAt(i).remove(packageName);
+            for (ArrayList<String> initiators : mImplicitlyQueryable.valueAt(i).values()) {
+                initiators.remove(packageName);
+            }
+        }
+
+        mQueriesViaIntent.remove(packageName);
+        for (List<String> declarators : mQueriesViaIntent.values()) {
+            declarators.remove(packageName);
+        }
+
+        mQueriesViaPackage.remove(packageName);
+    }
+
+    /**
+     * Returns true if the calling package should not be able to see the target package, false if no
+     * filtering should be done.
+     *
+     * @param callingUid       the uid of the caller attempting to access a package
+     * @param callingSetting   the setting attempting to access a package or null if it could not be
+     *                         found
+     * @param targetPkgSetting the package being accessed
+     * @param userId           the user in which this access is being attempted
+     */
+    public boolean shouldFilterApplication(int callingUid, @Nullable SettingBase callingSetting,
+            PackageSetting targetPkgSetting, int userId) {
+        if (callingUid < Process.FIRST_APPLICATION_UID) {
+            return false;
+        }
+        if (callingSetting == null) {
+            Slog.wtf(TAG, "No setting found for non system uid " + callingUid);
+            return true;
+        }
+        PackageSetting callingPkgSetting = null;
+        if (callingSetting instanceof PackageSetting) {
+            callingPkgSetting = (PackageSetting) callingSetting;
+            if (!shouldFilterApplicationInternal(callingPkgSetting, targetPkgSetting,
+                    userId)) {
+                // TODO: actually base this on a start / launch (not just a query)
+                markAppInteraction(callingPkgSetting, targetPkgSetting, userId);
+                return false;
+            }
+        } else if (callingSetting instanceof SharedUserSetting) {
+            final ArraySet<PackageSetting> packageSettings =
+                    ((SharedUserSetting) callingSetting).packages;
+            if (packageSettings != null && packageSettings.size() > 0) {
+                for (PackageSetting packageSetting : packageSettings) {
+                    if (!shouldFilterApplicationInternal(packageSetting, targetPkgSetting,
+                            userId)) {
+                        // TODO: actually base this on a start / launch (not just a query)
+                        markAppInteraction(packageSetting, targetPkgSetting, userId);
+                        return false;
+                    }
+                    if (callingPkgSetting == null && packageSetting.pkg != null) {
+                        callingPkgSetting = packageSetting;
+                    }
+                }
+            } else {
+                return true;
+            }
+        }
+        if (callingPkgSetting == null) {
+            Slog.wtf(TAG, "What... " + callingSetting);
+            return true;
+        }
+        final int mode = mAppOpsManager
+                .checkOpNoThrow(AppOpsManager.OP_QUERY_ALL_PACKAGES, callingUid,
+                        callingPkgSetting.pkg.packageName);
+        switch (mode) {
+            case AppOpsManager.MODE_DEFAULT:
+                // if default, let's rely on remote feature toggle to determine whether to
+                // actually filter
+                return mConfigProvider.isEnabled();
+            case AppOpsManager.MODE_ALLOWED:
+                // explicitly allowed to see all packages, don't filter
+                return false;
+            case AppOpsManager.MODE_ERRORED:
+                // deny / error: let's log so developer can audit usages
+                Slog.i(TAG, callingPkgSetting.pkg.packageName
+                        + " blocked from accessing " + targetPkgSetting.pkg.packageName);
+            case AppOpsManager.MODE_IGNORED:
+                // fall through
+            default:
+                return true;
+        }
+    }
+
+    private boolean shouldFilterApplicationInternal(
+            PackageSetting callingPkgSetting, PackageSetting targetPkgSetting, int userId) {
+        final String callingName = callingPkgSetting.pkg.packageName;
+        final String targetName = targetPkgSetting.pkg.packageName;
+        if (callingPkgSetting.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.R) {
+            return false;
+        }
+        if (isImplicitlyQueryableSystemApp(targetPkgSetting)) {
+            return false;
+        }
+        if (targetPkgSetting.pkg.mForceQueryable) {
+            return false;
+        }
+        if (mForceQueryable.contains(targetName)) {
+            return false;
+        }
+        if (mQueriesViaPackage.containsKey(callingName)
+                && mQueriesViaPackage.get(callingName).contains(
+                targetName)) {
+            // the calling package has explicitly declared the target package; allow
+            return false;
+        } else if (mQueriesViaIntent.containsKey(callingName)
+                && mQueriesViaIntent.get(callingName).contains(targetName)) {
+            return false;
+        }
+        if (mImplicitlyQueryable.get(userId) != null
+                && mImplicitlyQueryable.get(userId).containsKey(callingName)
+                && mImplicitlyQueryable.get(userId).get(callingName).contains(targetName)) {
+            return false;
+        }
+        try {
+            if (mPermissionManager.checkPermission(
+                    Manifest.permission.QUERY_ALL_PACKAGES, callingName, userId)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return false;
+            }
+        } catch (RemoteException e) {
+            return true;
+        }
+        return true;
+    }
+
+    private boolean isImplicitlyQueryableSystemApp(PackageSetting targetPkgSetting) {
+        return targetPkgSetting.isSystem() && (mSystemAppsQueryable
+                || mForceQueryableByDevice.contains(targetPkgSetting.pkg.packageName));
+    }
+
+    public interface ConfigProvider {
+        boolean isEnabled();
+    }
+
+}
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 8b9af7a..8f38026 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -112,7 +112,7 @@
     private final CookiePersistence mCookiePersistence;
 
     /** State for uninstalled instant apps */
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     private SparseArray<List<UninstalledInstantAppState>> mUninstalledInstantApps;
 
     /**
@@ -121,11 +121,11 @@
      * The value is a set of instant app UIDs.
      * UserID -> TargetAppId -> InstantAppId
      */
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     private SparseArray<SparseArray<SparseBooleanArray>> mInstantGrants;
 
     /** The set of all installed instant apps. UserID -> AppID */
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     private SparseArray<SparseBooleanArray> mInstalledInstantAppUids;
 
     public InstantAppRegistry(PackageManagerService service) {
@@ -133,7 +133,7 @@
         mCookiePersistence = new CookiePersistence(BackgroundThread.getHandler().getLooper());
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     public byte[] getInstantAppCookieLPw(@NonNull String packageName,
             @UserIdInt int userId) {
         // Only installed packages can get their own cookie
@@ -157,7 +157,7 @@
         return null;
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     public boolean setInstantAppCookieLPw(@NonNull String packageName,
             @Nullable byte[] cookie, @UserIdInt int userId) {
         if (cookie != null && cookie.length > 0) {
@@ -182,7 +182,7 @@
 
     private void persistInstantApplicationCookie(@Nullable byte[] cookie,
             @NonNull String packageName, @NonNull File cookieFile, @UserIdInt int userId) {
-        synchronized (mService.mPackages) {
+        synchronized (mService.mLock) {
             File appDir = getInstantApplicationDir(packageName, userId);
             if (!appDir.exists() && !appDir.mkdirs()) {
                 Slog.e(LOG_TAG, "Cannot create instant app cookie directory");
@@ -250,7 +250,7 @@
 
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     public @Nullable List<InstantAppInfo> getInstantAppsLPr(@UserIdInt int userId) {
         List<InstantAppInfo> installedApps = getInstalledInstantApplicationsLPr(userId);
         List<InstantAppInfo> uninstalledApps = getUninstalledInstantApplicationsLPr(userId);
@@ -263,7 +263,7 @@
         return uninstalledApps;
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     public void onPackageInstalledLPw(@NonNull PackageParser.Package pkg, @NonNull int[] userIds) {
         PackageSetting ps = (PackageSetting) pkg.mExtras;
         if (ps == null) {
@@ -334,7 +334,7 @@
         }
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     public void onPackageUninstalledLPw(@NonNull PackageParser.Package pkg,
             @NonNull int[] userIds) {
         PackageSetting ps = (PackageSetting) pkg.mExtras;
@@ -360,7 +360,7 @@
         }
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     public void onUserRemovedLPw(int userId) {
         if (mUninstalledInstantApps != null) {
             mUninstalledInstantApps.remove(userId);
@@ -399,7 +399,7 @@
         return instantGrantList.get(instantAppId);
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     public void grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent,
             int targetAppId, int instantAppId) {
         if (mInstalledInstantAppUids == null) {
@@ -434,7 +434,7 @@
         instantGrantList.put(instantAppId, true /*granted*/);
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     public void addInstantAppLPw(@UserIdInt int userId, int instantAppId) {
         if (mInstalledInstantAppUids == null) {
             mInstalledInstantAppUids = new SparseArray<>();
@@ -447,7 +447,7 @@
         instantAppList.put(instantAppId, true /*installed*/);
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     private void removeInstantAppLPw(@UserIdInt int userId, int instantAppId) {
         // remove from the installed list
         if (mInstalledInstantAppUids == null) {
@@ -473,7 +473,7 @@
         }
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     private void removeAppLPw(@UserIdInt int userId, int targetAppId) {
         // remove from the installed list
         if (mInstantGrants == null) {
@@ -486,7 +486,7 @@
         targetAppList.delete(targetAppId);
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     private void addUninstalledInstantAppLPw(@NonNull PackageParser.Package pkg,
             @UserIdInt int userId) {
         InstantAppInfo uninstalledApp = createInstantAppInfoForPackage(
@@ -541,13 +541,13 @@
         }
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     boolean hasInstantApplicationMetadataLPr(String packageName, int userId) {
         return hasUninstalledInstantAppStateLPr(packageName, userId)
                 || hasInstantAppMetadataLPr(packageName, userId);
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     public void deleteInstantApplicationMetadataLPw(@NonNull String packageName,
             @UserIdInt int userId) {
         removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) ->
@@ -564,7 +564,7 @@
         }
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     private void removeUninstalledInstantAppStateLPw(
             @NonNull Predicate<UninstalledInstantAppState> criteria, @UserIdInt int userId) {
         if (mUninstalledInstantApps == null) {
@@ -592,7 +592,7 @@
         }
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     private boolean hasUninstalledInstantAppStateLPr(String packageName, @UserIdInt int userId) {
         if (mUninstalledInstantApps == null) {
             return false;
@@ -685,7 +685,7 @@
         final long now = System.currentTimeMillis();
 
         // Prune first installed instant apps
-        synchronized (mService.mPackages) {
+        synchronized (mService.mLock) {
             allUsers = PackageManagerService.sUserManager.getUserIds();
 
             final int packageCount = mService.mPackages.size();
@@ -768,7 +768,7 @@
         }
 
         // Prune uninstalled instant apps
-        synchronized (mService.mPackages) {
+        synchronized (mService.mLock) {
             // TODO: Track last used time for uninstalled instant apps for better pruning
             for (int userId : UserManagerService.getInstance().getUserIds()) {
                 // Prune in-memory state
@@ -811,7 +811,7 @@
         return false;
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     private @Nullable List<InstantAppInfo> getInstalledInstantApplicationsLPr(
             @UserIdInt int userId) {
         List<InstantAppInfo> result = null;
@@ -866,7 +866,7 @@
         }
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     private @Nullable List<InstantAppInfo> getUninstalledInstantApplicationsLPr(
             @UserIdInt int userId) {
         List<UninstalledInstantAppState> uninstalledAppStates =
@@ -939,7 +939,7 @@
         return uninstalledAppState.mInstantAppInfo;
     }
 
-    @GuardedBy("mService.mPackages")
+    @GuardedBy("mService.mLock")
     private @Nullable List<UninstalledInstantAppState> getUninstalledInstantAppStatesLPr(
             @UserIdInt int userId) {
         List<UninstalledInstantAppState> uninstalledAppStates = null;
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index e5a2e77..d49ecdd 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -120,7 +120,7 @@
         }
         final List<PackageParser.Package> important;
         final List<PackageParser.Package> others;
-        synchronized (mPackageManagerService.mPackages) {
+        synchronized (mPackageManagerService.mLock) {
             // Important: the packages we need to run with ab-ota compiler-reason.
             important = PackageManagerServiceUtils.getPackagesForDexopt(
                     mPackageManagerService.mPackages.values(), mPackageManagerService,
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java
new file mode 100644
index 0000000..da45582
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java
@@ -0,0 +1,85 @@
+/*
+ * 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.pm;
+
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+@VisibleForTesting
+interface PackageAbiHelper {
+    /**
+     * Derive and set the location of native libraries for the given package,
+     * which varies depending on where and how the package was installed.
+     *
+     * WARNING: This API enables modifying of the package.
+     * TODO(b/137881067): Modify so that caller is responsible for setting pkg fields as necessary
+     */
+    void setNativeLibraryPaths(PackageParser.Package pkg, File appLib32InstallDir);
+
+    /**
+     * Calculate the abis and roots for a bundled app. These can uniquely
+     * be determined from the contents of the system partition, i.e whether
+     * it contains 64 or 32 bit shared libraries etc. We do not validate any
+     * of this information, and instead assume that the system was built
+     * sensibly.
+     *
+     * WARNING: This API enables modifying of the package.
+     * TODO(b/137881067): Modify so that caller is responsible for setting pkg fields as necessary
+     */
+    void setBundledAppAbisAndRoots(PackageParser.Package pkg,
+            PackageSetting pkgSetting);
+
+    /**
+     * Derive the ABI of a non-system package located at {@code scanFile}. This information
+     * is derived purely on the basis of the contents of {@code scanFile} and
+     * {@code cpuAbiOverride}.
+     *
+     * If {@code extractLibs} is true, native libraries are extracted from the app if required.
+     *
+     * WARNING: This API enables modifying of the package.
+     * TODO(b/137881067): Modify so that caller is responsible for setting pkg fields as necessary
+     */
+    void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride,
+            boolean extractLibs)
+            throws PackageManagerException;
+
+    /**
+     * Adjusts ABIs for a set of packages belonging to a shared user so that they all match.
+     * i.e, so that all packages can be run inside a single process if required.
+     *
+     * Optionally, callers can pass in a parsed package via {@code newPackage} in which case
+     * this function will either try and make the ABI for all packages in
+     * {@code packagesForUser} match {@code scannedPackage} or will update the ABI of
+     * {@code scannedPackage} to match the ABI selected for {@code packagesForUser}. This
+     * variant is used when installing or updating a package that belongs to a shared user.
+     *
+     * NOTE: We currently only match for the primary CPU abi string. Matching the secondary
+     * adds unnecessary complexity.
+     *
+     * WARNING: This API enables modifying of the package.
+     * TODO(b/137881067): Modify so that caller is responsible for setting pkg fields as necessary
+     */
+    @Nullable
+    List<String> adjustCpuAbisForSharedUser(
+            Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage);
+}
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
new file mode 100644
index 0000000..4ecc888
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -0,0 +1,542 @@
+/*
+ * 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.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+import static android.content.pm.PackageParser.isApkFile;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
+import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
+import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
+import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
+
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.os.Build;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.Trace;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.util.ArrayUtils;
+
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+final class PackageAbiHelperImpl implements PackageAbiHelper {
+
+    @Override
+    public void setNativeLibraryPaths(PackageParser.Package pkg, File appLib32InstallDir) {
+        final ApplicationInfo info = pkg.applicationInfo;
+        final String codePath = pkg.codePath;
+        final File codeFile = new File(codePath);
+        final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
+
+        info.nativeLibraryRootDir = null;
+        info.nativeLibraryRootRequiresIsa = false;
+        info.nativeLibraryDir = null;
+        info.secondaryNativeLibraryDir = null;
+
+        if (isApkFile(codeFile)) {
+            // Monolithic install
+            if (bundledApp) {
+                // If "/system/lib64/apkname" exists, assume that is the per-package
+                // native library directory to use; otherwise use "/system/lib/apkname".
+                final String apkRoot = calculateBundledApkRoot(info.sourceDir);
+                final boolean is64Bit = VMRuntime.is64BitInstructionSet(
+                        getPrimaryInstructionSet(info));
+
+                // This is a bundled system app so choose the path based on the ABI.
+                // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
+                // is just the default path.
+                final String apkName = deriveCodePathName(codePath);
+                final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
+                info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
+                        apkName).getAbsolutePath();
+
+                if (info.secondaryCpuAbi != null) {
+                    final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
+                    info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
+                            secondaryLibDir, apkName).getAbsolutePath();
+                }
+            } else {
+                final String apkName = deriveCodePathName(codePath);
+                info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
+                        .getAbsolutePath();
+            }
+
+            info.nativeLibraryRootRequiresIsa = false;
+            info.nativeLibraryDir = info.nativeLibraryRootDir;
+        } else {
+            // Cluster install
+            info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
+            info.nativeLibraryRootRequiresIsa = true;
+
+            info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
+                    getPrimaryInstructionSet(info)).getAbsolutePath();
+
+            if (info.secondaryCpuAbi != null) {
+                info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,
+                        VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath();
+            }
+        }
+    }
+
+    @Override
+    public void setBundledAppAbisAndRoots(PackageParser.Package pkg,
+            PackageSetting pkgSetting) {
+        final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
+
+        // If "/system/lib64/apkname" exists, assume that is the per-package
+        // native library directory to use; otherwise use "/system/lib/apkname".
+        final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir);
+        setBundledAppAbi(pkg, apkRoot, apkName);
+        // pkgSetting might be null during rescan following uninstall of updates
+        // to a bundled app, so accommodate that possibility.  The settings in
+        // that case will be established later from the parsed package.
+        //
+        // If the settings aren't null, sync them up with what we've just derived.
+        // note that apkRoot isn't stored in the package settings.
+        if (pkgSetting != null) {
+            pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
+            pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
+        }
+    }
+
+    /**
+     * Deduces the ABI of a bundled app and sets the relevant fields on the
+     * parsed pkg object.
+     *
+     * @param apkRoot the root of the installed apk, something like {@code /system} or
+     *                {@code /oem} under which system libraries are installed.
+     * @param apkName the name of the installed package.
+     */
+    private void setBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) {
+        final File codeFile = new File(pkg.codePath);
+
+        final boolean has64BitLibs;
+        final boolean has32BitLibs;
+        if (isApkFile(codeFile)) {
+            // Monolithic install
+            has64BitLibs =
+                    (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists();
+            has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists();
+        } else {
+            // Cluster install
+            final File rootDir = new File(codeFile, LIB_DIR_NAME);
+            if (!ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS)
+                    && !TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) {
+                final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]);
+                has64BitLibs = (new File(rootDir, isa)).exists();
+            } else {
+                has64BitLibs = false;
+            }
+            if (!ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS)
+                    && !TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) {
+                final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]);
+                has32BitLibs = (new File(rootDir, isa)).exists();
+            } else {
+                has32BitLibs = false;
+            }
+        }
+
+        if (has64BitLibs && !has32BitLibs) {
+            // The package has 64 bit libs, but not 32 bit libs. Its primary
+            // ABI should be 64 bit. We can safely assume here that the bundled
+            // native libraries correspond to the most preferred ABI in the list.
+
+            pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
+            pkg.applicationInfo.secondaryCpuAbi = null;
+        } else if (has32BitLibs && !has64BitLibs) {
+            // The package has 32 bit libs but not 64 bit libs. Its primary
+            // ABI should be 32 bit.
+
+            pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
+            pkg.applicationInfo.secondaryCpuAbi = null;
+        } else if (has32BitLibs && has64BitLibs) {
+            // The application has both 64 and 32 bit bundled libraries. We check
+            // here that the app declares multiArch support, and warn if it doesn't.
+            //
+            // We will be lenient here and record both ABIs. The primary will be the
+            // ABI that's higher on the list, i.e, a device that's configured to prefer
+            // 64 bit apps will see a 64 bit primary ABI,
+
+            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) {
+                Slog.e(PackageManagerService.TAG,
+                        "Package " + pkg + " has multiple bundled libs, but is not multiarch.");
+            }
+
+            if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) {
+                pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
+                pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
+            } else {
+                pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
+                pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
+            }
+        } else {
+            pkg.applicationInfo.primaryCpuAbi = null;
+            pkg.applicationInfo.secondaryCpuAbi = null;
+        }
+    }
+
+    @Override
+    public void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride,
+            boolean extractLibs)
+            throws PackageManagerException {
+        // Give ourselves some initial paths; we'll come back for another
+        // pass once we've determined ABI below.
+        setNativeLibraryPaths(pkg, PackageManagerService.sAppLib32InstallDir);
+
+        // We shouldn't attempt to extract libs from system app when it was not updated.
+        if (PackageManagerService.isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
+            extractLibs = false;
+        }
+
+        final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir;
+        final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa;
+
+        NativeLibraryHelper.Handle handle = null;
+        try {
+            handle = NativeLibraryHelper.Handle.create(pkg);
+            // TODO(multiArch): This can be null for apps that didn't go through the
+            // usual installation process. We can calculate it again, like we
+            // do during install time.
+            //
+            // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally
+            // unnecessary.
+            final File nativeLibraryRoot = new File(nativeLibraryRootStr);
+
+            // Null out the abis so that they can be recalculated.
+            pkg.applicationInfo.primaryCpuAbi = null;
+            pkg.applicationInfo.secondaryCpuAbi = null;
+            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0) {
+                // Warn if we've set an abiOverride for multi-lib packages..
+                // By definition, we need to copy both 32 and 64 bit libraries for
+                // such packages.
+                if (pkg.cpuAbiOverride != null
+                        && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
+                    Slog.w(PackageManagerService.TAG,
+                            "Ignoring abiOverride for multi arch application.");
+                }
+
+                int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
+                int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
+                if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+                    if (extractLibs) {
+                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+                        abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+                                nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
+                                useIsaSpecificSubdirs);
+                    } else {
+                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
+                        abi32 = NativeLibraryHelper.findSupportedAbi(
+                                handle, Build.SUPPORTED_32_BIT_ABIS);
+                    }
+                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                }
+
+                // Shared library native code should be in the APK zip aligned
+                if (abi32 >= 0 && pkg.isLibrary() && extractLibs) {
+                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                            "Shared library native lib extraction not supported");
+                }
+
+                maybeThrowExceptionForMultiArchCopy(
+                        "Error unpackaging 32 bit native libs for multiarch app.", abi32);
+
+                if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
+                    if (extractLibs) {
+                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+                        abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+                                nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
+                                useIsaSpecificSubdirs);
+                    } else {
+                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
+                        abi64 = NativeLibraryHelper.findSupportedAbi(
+                                handle, Build.SUPPORTED_64_BIT_ABIS);
+                    }
+                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                }
+
+                maybeThrowExceptionForMultiArchCopy(
+                        "Error unpackaging 64 bit native libs for multiarch app.", abi64);
+
+                if (abi64 >= 0) {
+                    // Shared library native libs should be in the APK zip aligned
+                    if (extractLibs && pkg.isLibrary()) {
+                        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                                "Shared library native lib extraction not supported");
+                    }
+                    pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
+                }
+
+                if (abi32 >= 0) {
+                    final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
+                    if (abi64 >= 0) {
+                        if (pkg.use32bitAbi) {
+                            pkg.applicationInfo.secondaryCpuAbi =
+                                    pkg.applicationInfo.primaryCpuAbi;
+                            pkg.applicationInfo.primaryCpuAbi = abi;
+                        } else {
+                            pkg.applicationInfo.secondaryCpuAbi = abi;
+                        }
+                    } else {
+                        pkg.applicationInfo.primaryCpuAbi = abi;
+                    }
+                }
+            } else {
+                String[] abiList = (cpuAbiOverride != null)
+                        ? new String[]{cpuAbiOverride} : Build.SUPPORTED_ABIS;
+
+                // Enable gross and lame hacks for apps that are built with old
+                // SDK tools. We must scan their APKs for renderscript bitcode and
+                // not launch them if it's present. Don't bother checking on devices
+                // that don't have 64 bit support.
+                boolean needsRenderScriptOverride = false;
+                if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null
+                        && NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+                    abiList = Build.SUPPORTED_32_BIT_ABIS;
+                    needsRenderScriptOverride = true;
+                }
+
+                final int copyRet;
+                if (extractLibs) {
+                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+                    copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+                            nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
+                } else {
+                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
+                    copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
+                }
+                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+                if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
+                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                            "Error unpackaging native libs for app, errorCode=" + copyRet);
+                }
+
+                if (copyRet >= 0) {
+                    // Shared libraries that have native libs must be multi-architecture
+                    if (pkg.isLibrary()) {
+                        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                                "Shared library with native libs must be multiarch");
+                    }
+                    pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
+                } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES
+                        && cpuAbiOverride != null) {
+                    pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
+                } else if (needsRenderScriptOverride) {
+                    pkg.applicationInfo.primaryCpuAbi = abiList[0];
+                }
+            }
+        } catch (IOException ioe) {
+            Slog.e(PackageManagerService.TAG, "Unable to get canonical file " + ioe.toString());
+        } finally {
+            IoUtils.closeQuietly(handle);
+        }
+
+        // Now that we've calculated the ABIs and determined if it's an internal app,
+        // we will go ahead and populate the nativeLibraryPath.
+        setNativeLibraryPaths(pkg, PackageManagerService.sAppLib32InstallDir);
+    }
+
+    /**
+     * Adjusts ABIs for a set of packages belonging to a shared user so that they all match.
+     * i.e, so that all packages can be run inside a single process if required.
+     *
+     * Optionally, callers can pass in a parsed package via {@code newPackage} in which case
+     * this function will either try and make the ABI for all packages in
+     * {@code packagesForUser} match {@code scannedPackage} or will update the ABI of
+     * {@code scannedPackage} to match the ABI selected for {@code packagesForUser}. This
+     * variant is used when installing or updating a package that belongs to a shared user.
+     *
+     * NOTE: We currently only match for the primary CPU abi string. Matching the secondary
+     * adds unnecessary complexity.
+     */
+    @Override
+    @Nullable
+    public List<String> adjustCpuAbisForSharedUser(
+            Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) {
+        List<String> changedAbiCodePath = null;
+        String requiredInstructionSet = null;
+        if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
+            requiredInstructionSet = VMRuntime.getInstructionSet(
+                    scannedPackage.applicationInfo.primaryCpuAbi);
+        }
+
+        PackageSetting requirer = null;
+        for (PackageSetting ps : packagesForUser) {
+            // If packagesForUser contains scannedPackage, we skip it. This will happen
+            // when scannedPackage is an update of an existing package. Without this check,
+            // we will never be able to change the ABI of any package belonging to a shared
+            // user, even if it's compatible with other packages.
+            if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) {
+                if (ps.primaryCpuAbiString == null) {
+                    continue;
+                }
+
+                final String instructionSet =
+                        VMRuntime.getInstructionSet(ps.primaryCpuAbiString);
+                if (requiredInstructionSet != null
+                        && !instructionSet.equals(requiredInstructionSet)) {
+                    // We have a mismatch between instruction sets (say arm vs arm64) warn about
+                    // this but there's not much we can do.
+                    String errorMessage = "Instruction set mismatch, "
+                            + ((requirer == null) ? "[caller]" : requirer)
+                            + " requires " + requiredInstructionSet + " whereas " + ps
+                            + " requires " + instructionSet;
+                    Slog.w(PackageManagerService.TAG, errorMessage);
+                }
+
+                if (requiredInstructionSet == null) {
+                    requiredInstructionSet = instructionSet;
+                    requirer = ps;
+                }
+            }
+        }
+
+        if (requiredInstructionSet != null) {
+            String adjustedAbi;
+            if (requirer != null) {
+                // requirer != null implies that either scannedPackage was null or that
+                // scannedPackage did not require an ABI, in which case we have to adjust
+                // scannedPackage to match the ABI of the set (which is the same as
+                // requirer's ABI)
+                adjustedAbi = requirer.primaryCpuAbiString;
+                if (scannedPackage != null) {
+                    scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi;
+                }
+            } else {
+                // requirer == null implies that we're updating all ABIs in the set to
+                // match scannedPackage.
+                adjustedAbi = scannedPackage.applicationInfo.primaryCpuAbi;
+            }
+
+            for (PackageSetting ps : packagesForUser) {
+                if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) {
+                    if (ps.primaryCpuAbiString != null) {
+                        continue;
+                    }
+
+                    ps.primaryCpuAbiString = adjustedAbi;
+                    if (ps.pkg != null && ps.pkg.applicationInfo != null
+                            && !TextUtils.equals(
+                            adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) {
+                        ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
+                        if (PackageManagerService.DEBUG_ABI_SELECTION) {
+                            Slog.i(PackageManagerService.TAG,
+                                    "Adjusting ABI for " + ps.name + " to " + adjustedAbi
+                                            + " (requirer="
+                                            + (requirer != null ? requirer.pkg : "null")
+                                            + ", scannedPackage="
+                                            + (scannedPackage != null ? scannedPackage : "null")
+                                            + ")");
+                        }
+                        if (changedAbiCodePath == null) {
+                            changedAbiCodePath = new ArrayList<>();
+                        }
+                        changedAbiCodePath.add(ps.codePathString);
+                    }
+                }
+            }
+        }
+        return changedAbiCodePath;
+    }
+
+    private static String calculateBundledApkRoot(final String codePathString) {
+        final File codePath = new File(codePathString);
+        final File codeRoot;
+        if (FileUtils.contains(Environment.getRootDirectory(), codePath)) {
+            codeRoot = Environment.getRootDirectory();
+        } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) {
+            codeRoot = Environment.getOemDirectory();
+        } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) {
+            codeRoot = Environment.getVendorDirectory();
+        } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
+            codeRoot = Environment.getOdmDirectory();
+        } else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) {
+            codeRoot = Environment.getProductDirectory();
+        } else if (FileUtils.contains(Environment.getSystemExtDirectory(), codePath)) {
+            codeRoot = Environment.getSystemExtDirectory();
+        } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
+            codeRoot = Environment.getOdmDirectory();
+        } else {
+            // Unrecognized code path; take its top real segment as the apk root:
+            // e.g. /something/app/blah.apk => /something
+            try {
+                File f = codePath.getCanonicalFile();
+                File parent = f.getParentFile();    // non-null because codePath is a file
+                File tmp;
+                while ((tmp = parent.getParentFile()) != null) {
+                    f = parent;
+                    parent = tmp;
+                }
+                codeRoot = f;
+                Slog.w(PackageManagerService.TAG, "Unrecognized code path "
+                        + codePath + " - using " + codeRoot);
+            } catch (IOException e) {
+                // Can't canonicalize the code path -- shenanigans?
+                Slog.w(PackageManagerService.TAG, "Can't canonicalize code path " + codePath);
+                return Environment.getRootDirectory().getPath();
+            }
+        }
+        return codeRoot.getPath();
+    }
+
+    // Utility method that returns the relative package path with respect
+    // to the installation directory. Like say for /data/data/com.test-1.apk
+    // string com.test-1 is returned.
+    private static String deriveCodePathName(String codePath) {
+        if (codePath == null) {
+            return null;
+        }
+        final File codeFile = new File(codePath);
+        final String name = codeFile.getName();
+        if (codeFile.isDirectory()) {
+            return name;
+        } else if (name.endsWith(".apk") || name.endsWith(".tmp")) {
+            final int lastDot = name.lastIndexOf('.');
+            return name.substring(0, lastDot);
+        } else {
+            Slog.w(PackageManagerService.TAG, "Odd, " + codePath + " doesn't look like an APK");
+            return null;
+        }
+    }
+
+    private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws
+            PackageManagerException {
+        if (copyRet < 0) {
+            if (copyRet != PackageManager.NO_NATIVE_LIBRARIES
+                    && copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
+                throw new PackageManagerException(copyRet, message);
+            }
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c72b38a..4ad3fa1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -94,14 +94,12 @@
 
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
-import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
 import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
 import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
-import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
 import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
 import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
@@ -277,6 +275,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
@@ -314,6 +313,8 @@
 import com.android.server.pm.permission.PermissionManagerService;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.permission.PermissionsState;
+import com.android.server.policy.PermissionPolicyInternal;
+import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
 import com.android.server.security.VerityUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 import com.android.server.utils.TimingsTraceAndSlog;
@@ -429,7 +430,7 @@
     // user, but by default initialize to this.
     public static final boolean DEBUG_DEXOPT = false;
 
-    private static final boolean DEBUG_ABI_SELECTION = false;
+    static final boolean DEBUG_ABI_SELECTION = false;
     private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
     private static final boolean DEBUG_APP_DATA = false;
 
@@ -641,7 +642,7 @@
     final boolean mIsPreNMR1Upgrade;
     final boolean mIsPreQUpgrade;
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private boolean mDexOptDialogShown;
 
     // Used for privilege escalation. MUST NOT BE CALLED WITH mPackages
@@ -653,7 +654,8 @@
     private static final File sAppInstallDir =
             new File(Environment.getDataDirectory(), "app");
     /** Directory where installed application's 32-bit native libraries are copied. */
-    private static final File sAppLib32InstallDir =
+    @VisibleForTesting
+    static final File sAppLib32InstallDir =
             new File(Environment.getDataDirectory(), "app-lib");
 
     // ----------------------------------------------------------------
@@ -665,15 +667,18 @@
 
     // ----------------------------------------------------------------
 
-    // Keys are String (package name), values are Package.  This also serves
-    // as the lock for the global state.  Methods that must be called with
-    // this lock held have the prefix "LP".
-    @GuardedBy("mPackages")
+    // Lock for global state used when modifying package state or settings.
+    // Methods that must be called with this lock held have
+    // the suffix "Locked". Some methods may use the legacy the suffix "LP"
+    final Object mLock;
+
+    // Keys are String (package name), values are Package.
+    @GuardedBy("mLock")
     final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<>();
 
     // Keys are isolated uids and values are the uid of the application
     // that created the isolated proccess.
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     final SparseIntArray mIsolatedOwners = new SparseIntArray();
 
     /**
@@ -692,7 +697,7 @@
      */
     boolean mPromoteSystemApps;
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     final Settings mSettings;
 
     /**
@@ -702,7 +707,7 @@
      *
      * @see PackageFreezer
      */
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     final ArraySet<String> mFrozenPackages = new ArraySet<>();
 
     final ProtectedPackages mProtectedPackages;
@@ -719,31 +724,57 @@
 
     private final InstantAppRegistry mInstantAppRegistry;
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     int mChangedPackagesSequenceNumber;
     /**
      * List of changed [installed, removed or updated] packages.
      * mapping from user id -> sequence number -> package name
      */
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     final SparseArray<SparseArray<String>> mChangedPackages = new SparseArray<>();
     /**
      * The sequence number of the last change to a package.
      * mapping from user id -> package name -> sequence number
      */
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers = new SparseArray<>();
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>();
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private final SparseIntArray mDefaultPermissionsGrantedUsers = new SparseIntArray();
 
     private final ModuleInfoProvider mModuleInfoProvider;
 
     private final ApexManager mApexManager;
 
+    private final Injector mInjector;
+
+    /**
+     * Unit tests will instantiate and / or extend to mock dependencies / behaviors.
+     */
+    @VisibleForTesting
+    static class Injector {
+        private final UserManagerInternal mUserManager;
+        private final PackageAbiHelper mAbiHelper;
+
+        Injector(UserManagerInternal userManager, PackageAbiHelper abiHelper) {
+            mUserManager = userManager;
+            mAbiHelper = abiHelper;
+        }
+
+        public UserManagerInternal getUserManager() {
+            return mUserManager;
+        }
+
+        public PackageAbiHelper getAbiHelper() {
+            return mAbiHelper;
+        }
+    }
+
+    private final AppsFilter mAppsFilter;
+
     class PackageParserCallback implements PackageParser.Callback {
         @Override public final boolean hasFeature(String feature) {
             return PackageManagerService.this.hasSystemFeature(feature, 0);
@@ -813,7 +844,7 @@
         String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
             List<PackageParser.Package> overlayPackages;
             synchronized (mInstallLock) {
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     overlayPackages = getStaticOverlayPackages(
                             mPackages.values(), targetPackageName);
                 }
@@ -837,7 +868,7 @@
         List<PackageParser.Package> mOverlayPackages = null;
 
         void findStaticOverlayPackages() {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 for (PackageParser.Package p : mPackages.values()) {
                     if (p.mOverlayIsStatic) {
                         if (mOverlayPackages == null) {
@@ -1021,7 +1052,7 @@
                     PackageParser.ActivityIntentInfo filter = filters.get(m);
                     domainsSet.addAll(filter.getHostsList());
                 }
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     if (mSettings.createIntentFilterVerificationIfNeededLPw(
                             packageName, domainsSet) != null) {
                         scheduleWriteSettingsLocked();
@@ -1089,7 +1120,7 @@
             final String packageName = ivs.getPackageName();
             IntentFilterVerificationInfo ivi;
 
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 ivi = mSettings.getIntentFilterVerificationLPr(packageName);
             }
             if (ivi == null) {
@@ -1098,7 +1129,7 @@
                 return;
             }
 
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 if (verified) {
                     ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
                 } else {
@@ -1210,7 +1241,7 @@
             IntentFilterVerificationState ivs = new IntentFilterVerificationState(
                     verifierUid, userId, packageName);
             ivs.setPendingState();
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mIntentFilterVerificationStates.append(verificationId, ivs);
                 mCurrentIntentFilterVerifications.add(verificationId);
             }
@@ -1423,7 +1454,7 @@
                     int size = 0;
                     int uids[];
                     Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
-                    synchronized (mPackages) {
+                    synchronized (mLock) {
                         size = mPendingBroadcasts.size();
                         if (size <= 0) {
                             // Nothing to be done. Just return
@@ -1531,7 +1562,7 @@
                 } break;
                 case WRITE_SETTINGS: {
                     Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
-                    synchronized (mPackages) {
+                    synchronized (mLock) {
                         removeMessages(WRITE_SETTINGS);
                         removeMessages(WRITE_PACKAGE_RESTRICTIONS);
                         mSettings.writeLPr();
@@ -1541,7 +1572,7 @@
                 } break;
                 case WRITE_PACKAGE_RESTRICTIONS: {
                     Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
-                    synchronized (mPackages) {
+                    synchronized (mLock) {
                         removeMessages(WRITE_PACKAGE_RESTRICTIONS);
                         for (int userId : mDirtyUsers) {
                             mSettings.writePackageRestrictionsLPr(userId);
@@ -1552,7 +1583,7 @@
                 } break;
                 case WRITE_PACKAGE_LIST: {
                     Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
-                    synchronized (mPackages) {
+                    synchronized (mLock) {
                         removeMessages(WRITE_PACKAGE_LIST);
                         mSettings.writePackageListLPr(msg.arg1);
                     }
@@ -1789,7 +1820,7 @@
                         res.pkg, callingUid);
             }
 
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers);
             }
 
@@ -1950,7 +1981,7 @@
                     if (packageIsBrowser(packageName, userId)) {
                         // If this browser is restored from user's backup, do not clear
                         // default-browser state for this user
-                        synchronized (mPackages) {
+                        synchronized (mLock) {
                             final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
                             if (pkgSetting.getInstallReason(userId)
                                     != PackageManager.INSTALL_REASON_DEVICE_RESTORE) {
@@ -2007,8 +2038,8 @@
             // survive long enough to benefit of background optimizations.
             for (int userId : firstUserIds) {
                 PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
-                // There's a race currently where some install events may interleave with an uninstall.
-                // This can lead to package info being null (b/36642664).
+                // There's a race currently where some install events may interleave with an
+                // uninstall. This can lead to package info being null (b/36642664).
                 if (info != null) {
                     mDexManager.notifyPackageInstalled(info, userId);
                 }
@@ -2030,7 +2061,8 @@
 
         for (String packageName : packages) {
             PackageSetting setting = mSettings.mPackages.get(packageName);
-            if (setting != null && filterAppAccessLPr(setting, callingUid, callingUserId)) {
+            if (setting != null
+                    && shouldFilterApplicationLocked(setting, callingUid, callingUserId)) {
                 notifyInstallObserver(packageName);
             }
         }
@@ -2078,6 +2110,7 @@
      * external/removable/unprotected storage.
      * @return {@link StorageEnum#TYPE_UNKNOWN} if the package is not stored externally or the
      * corresponding {@link StorageEnum} storage type value if it is.
+     * corresponding {@link StorageEnum} storage type value if it is.
      */
     private static int getPackageExternalStorageType(VolumeInfo packageVolume,
             boolean packageIsExternal) {
@@ -2130,7 +2163,7 @@
             }
 
             // Remove any apps installed on the forgotten volume
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(fsUuid);
                 for (PackageSetting ps : packages) {
                     Slog.d(TAG, "Destroying " + ps.name + " because volume was forgotten");
@@ -2206,9 +2239,10 @@
             boolean factoryTest, boolean onlyCore) {
         // Self-check for initial settings.
         PackageManagerServiceCompilerMapping.checkProperties();
+        final Object packageLock = new Object();
 
         PackageManagerService m = new PackageManagerService(context, installer,
-                factoryTest, onlyCore);
+                factoryTest, onlyCore, packageLock);
         m.enableSystemUserPackages();
         ServiceManager.addService("package", m);
         final PackageManagerNative pmn = m.new PackageManagerNative();
@@ -2241,7 +2275,7 @@
         List<String> allAps = queryHelper.queryApps(0, /* systemAppsOnly */ false,
                 UserHandle.SYSTEM);
         final int allAppsSize = allAps.size();
-        synchronized (mPackages) {
+        synchronized (mLock) {
             for (int i = 0; i < allAppsSize; i++) {
                 String pName = allAps.get(i);
                 PackageSetting pkgSetting = mSettings.mPackages.get(pName);
@@ -2300,11 +2334,12 @@
     }
 
     public PackageManagerService(Context context, Installer installer, boolean factoryTest,
-            boolean onlyCore) {
+            boolean onlyCore, Object packageLock) {
         final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
                 Trace.TRACE_TAG_PACKAGE_MANAGER);
         t.traceBegin("create package manager");
-        LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
+        mLock = packageLock;
+        LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                 SystemClock.uptimeMillis());
 
@@ -2313,7 +2348,6 @@
         }
 
         mContext = context;
-
         mFactoryTest = factoryTest;
         mOnlyCore = onlyCore;
         mMetrics = new DisplayMetrics();
@@ -2323,23 +2357,28 @@
         t.traceBegin("createSubComponents");
         // CHECKSTYLE:OFF IndentationCheck
         synchronized (mInstallLock) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // Expose private service for system components to use.
             LocalServices.addService(
                     PackageManagerInternal.class, new PackageManagerInternalImpl());
             sUserManager = new UserManagerService(context, this,
-                    new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
+                    new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore),
+                    mLock);
             mComponentResolver = new ComponentResolver(sUserManager,
                     LocalServices.getService(PackageManagerInternal.class),
-                    mPackages);
+                    mLock);
             mPermissionManager = PermissionManagerService.create(context,
-                    mPackages /*externalLock*/);
+                    mLock /*externalLock*/);
             mPermissionManagerService =
                     (IPermissionManager) ServiceManager.getService("permissionmgr");
             mSettings = new Settings(Environment.getDataDirectory(),
-                    mPermissionManager.getPermissionSettings(), mPackages);
+                    mPermissionManager.getPermissionSettings(), mLock);
         }
         }
+
+        // TODO(b/137961986): We should pass this via constructor, but would first need to create
+        // a packages lock that could also be passed in.
+        mInjector = new Injector(getUserManagerInternal(), new PackageAbiHelperImpl());
         // CHECKSTYLE:ON IndentationCheck
         t.traceEnd();
 
@@ -2397,10 +2436,12 @@
         mProtectedPackages = new ProtectedPackages(mContext);
 
         mApexManager = ApexManager.create(context);
+        mAppsFilter = AppsFilter.create(context);
+
         // CHECKSTYLE:OFF IndentationCheck
         synchronized (mInstallLock) {
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             mHandlerThread = new ServiceThread(TAG,
                     Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
             mHandlerThread.start();
@@ -3048,7 +3089,8 @@
                 // the rest of the commands above) because there's precious little we
                 // can do about it. A settings error is reported, though.
                 final List<String> changedAbiCodePath =
-                        adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/);
+                        mInjector.getAbiHelper().adjustCpuAbisForSharedUser(
+                                setting.packages, null /*scannedPackage*/);
                 if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
                     for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
                         final String codePathString = changedAbiCodePath.get(i);
@@ -3131,7 +3173,7 @@
                 int count = 0;
                 for (String pkgName : deferPackages) {
                     PackageParser.Package pkg = null;
-                    synchronized (mPackages) {
+                    synchronized (mLock) {
                         PackageSetting ps = mSettings.getPackageLPr(pkgName);
                         if (ps != null && ps.getInstalled(UserHandle.USER_SYSTEM)) {
                             pkg = ps.pkg;
@@ -3278,7 +3320,7 @@
                 MetricsLogger.histogram(null, "ota_package_manager_init_time",
                         (int) (SystemClock.uptimeMillis() - startTime));
             }
-        } // synchronized (mPackages)
+        } // synchronized (mLock)
         } // synchronized (mInstallLock)
         // CHECKSTYLE:ON IndentationCheck
 
@@ -3294,7 +3336,7 @@
         // The initial scanning above does many calls into installd while
         // holding the mPackages lock, but we're mostly interested in yelling
         // once we have a booted system.
-        mInstaller.setWarnIfHeld(mPackages);
+        mInstaller.setWarnIfHeld(mLock);
 
         PackageParser.readConfigUseRoundIcon(mContext.getResources());
 
@@ -3376,7 +3418,7 @@
             try (PackageFreezer freezer =
                     freezePackage(stubPkg.packageName, "setEnabledSetting")) {
                 pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/);
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     prepareAppDataAfterInstallLIF(pkg);
                     try {
                         updateSharedLibrariesLocked(pkg, null, mPackages);
@@ -3390,7 +3432,7 @@
                 // Whoops! Something went very wrong; roll back to the stub and disable the package
                 try (PackageFreezer freezer =
                         freezePackage(stubPkg.packageName, "setEnabledSetting")) {
-                    synchronized (mPackages) {
+                    synchronized (mLock) {
                         // NOTE: Ensure the system package is enabled; even for a compressed stub.
                         // If we don't, installing the system package fails during scan
                         enableSystemPackageLPw(stubPkg);
@@ -3403,7 +3445,7 @@
                     Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.packageName, pme);
                 } finally {
                     // Disable the package; the stub by itself is not runnable
-                    synchronized (mPackages) {
+                    synchronized (mLock) {
                         final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName);
                         if (stubPs != null) {
                             stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED,
@@ -3433,7 +3475,7 @@
         if (scanFile == null) {
             throw new PackageManagerException("Unable to decompress stub at " + stubPkg.codePath);
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             mSettings.disableSystemPackageLPw(stubPkg.packageName, true /*replaced*/);
         }
         removePackageLI(stubPkg, true /*chatty*/);
@@ -3510,7 +3552,7 @@
         return dstCodePath;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void updateInstantAppInstallerLocked(String modifiedPackage) {
         // we're only interested in updating the installer appliction when 1) it's not
         // already set or 2) the modified package is the installer
@@ -3631,7 +3673,7 @@
     }
 
     private @NonNull String getRequiredSharedLibraryLPr(String name, int version) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(name, version);
             if (libraryInfo == null) {
                 throw new IllegalStateException("Missing required shared library:" + name);
@@ -3731,7 +3773,7 @@
         if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
             return null;
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final Pair<ComponentName, String> instantAppResolver = getInstantAppResolverLPr();
             if (instantAppResolver == null) {
                 return null;
@@ -3796,7 +3838,7 @@
         return null;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private @Nullable ActivityInfo getInstantAppInstallerLPr() {
         String[] orderedActions = Build.IS_ENG
                 ? new String[]{
@@ -3863,7 +3905,7 @@
         return matches.get(0).getComponentInfo().getComponentName();
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void primeDomainVerificationsLPw(int userId) {
         if (DEBUG_DOMAIN_VERIFICATION) {
             Slog.d(TAG, "Priming domain verifications in user " + userId);
@@ -3993,7 +4035,7 @@
         //     and 2) ephemeral apps that have explicitly interacted with it
         //   * Ephemeral apps can only see their own data and exposed installed apps
         //   * Holding a signature permission allows seeing instant apps
-        if (filterAppAccessLPr(ps, callingUid, userId)) {
+        if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
             return null;
         }
 
@@ -4058,9 +4100,9 @@
             throw new SecurityException("Instant applications don't have access to this method");
         }
         final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
+            if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
                 throw new SecurityException("Package " + packageName + " was not found!");
             }
 
@@ -4089,11 +4131,11 @@
         final int callingUid = Binder.getCallingUid();
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /*requireFullPermission*/, false /*checkShell*/, "is package available");
-        synchronized (mPackages) {
+        synchronized (mLock) {
             PackageParser.Package p = mPackages.get(packageName);
             if (p != null) {
                 final PackageSetting ps = (PackageSetting) p.mExtras;
-                if (filterAppAccessLPr(ps, callingUid, userId)) {
+                if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                     return false;
                 }
                 if (ps != null) {
@@ -4134,7 +4176,7 @@
                 false /* requireFullPermission */, false /* checkShell */, "get package info");
 
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // Normalize package name to handle renamed packages and static libs
             packageName = resolveInternalPackageNameLPr(packageName, versionCode);
 
@@ -4150,7 +4192,7 @@
                     if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
                         return null;
                     }
-                    if (filterAppAccessLPr(ps, filterCallingUid, userId)) {
+                    if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
                         return null;
                     }
                     return generatePackageInfo(ps, flags, userId);
@@ -4168,7 +4210,7 @@
                 if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
                     return null;
                 }
-                if (ps != null && filterAppAccessLPr(ps, filterCallingUid, userId)) {
+                if (ps != null && shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
                     return null;
                 }
                 return generatePackageInfo((PackageSetting)p.mExtras, flags, userId);
@@ -4179,7 +4221,7 @@
                 if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
                     return null;
                 }
-                if (filterAppAccessLPr(ps, filterCallingUid, userId)) {
+                if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
                     return null;
                 }
                 return generatePackageInfo(ps, flags, userId);
@@ -4250,8 +4292,8 @@
      *
      * @see #canViewInstantApps(int, int)
      */
-    @GuardedBy("mPackages")
-    private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid,
+    @GuardedBy("mLock")
+    private boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
             @Nullable ComponentName component, @ComponentType int componentType, int userId) {
         // if we're in an isolated process, get the real calling UID
         if (Process.isIsolated(callingUid)) {
@@ -4302,18 +4344,21 @@
             return !mInstantAppRegistry.isInstantAccessGranted(
                     userId, UserHandle.getAppId(callingUid), ps.appId);
         }
-        return false;
+        int appId = UserHandle.getAppId(callingUid);
+        final SettingBase callingPs = mSettings.getSettingLPr(appId);
+        return mAppsFilter.shouldFilterApplication(callingUid, callingPs, ps, userId);
     }
 
     /**
-     * @see #filterAppAccessLPr(PackageSetting, int, ComponentName, int, int)
+     * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
      */
-    @GuardedBy("mPackages")
-    private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, int userId) {
-        return filterAppAccessLPr(ps, callingUid, null, TYPE_UNKNOWN, userId);
+    @GuardedBy("mLock")
+    private boolean shouldFilterApplicationLocked(
+            @Nullable PackageSetting ps, int callingUid, int userId) {
+        return shouldFilterApplicationLocked(ps, callingUid, null, TYPE_UNKNOWN, userId);
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
             int flags) {
         // Callers can access only the libs they depend on, otherwise they need to explicitly
@@ -4376,7 +4421,7 @@
         }
         final String[] out = new String[names.length];
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final int callingUserId = UserHandle.getUserId(callingUid);
             final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId);
             for (int i=names.length-1; i>=0; i--) {
@@ -4403,7 +4448,7 @@
         }
         final String[] out = new String[names.length];
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final int callingUserId = UserHandle.getUserId(callingUid);
             final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId);
             for (int i=names.length-1; i>=0; i--) {
@@ -4433,11 +4478,11 @@
                 false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid");
 
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageParser.Package p = mPackages.get(packageName);
             if (p != null && p.isMatch(flags)) {
                 PackageSetting ps = (PackageSetting) p.mExtras;
-                if (filterAppAccessLPr(ps, callingUid, userId)) {
+                if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                     return -1;
                 }
                 return UserHandle.getUid(userId, p.applicationInfo.uid);
@@ -4445,7 +4490,7 @@
             if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
                 if (ps != null && ps.isMatch(flags)
-                        && !filterAppAccessLPr(ps, callingUid, userId)) {
+                        && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
                     return UserHandle.getUid(userId, ps.appId);
                 }
             }
@@ -4463,11 +4508,11 @@
                 false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids");
 
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageParser.Package p = mPackages.get(packageName);
             if (p != null && p.isMatch(flags)) {
                 PackageSetting ps = (PackageSetting) p.mExtras;
-                if (filterAppAccessLPr(ps, callingUid, userId)) {
+                if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                     return null;
                 }
                 // TODO: Shouldn't this be checking for package installed state for userId and
@@ -4477,7 +4522,7 @@
             if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
                 if (ps != null && ps.isMatch(flags)
-                        && !filterAppAccessLPr(ps, callingUid, userId)) {
+                        && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
                     return ps.getPermissionsState().computeGids(userId);
                 }
             }
@@ -4497,7 +4542,7 @@
         return null;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
             int filterCallingUid, int userId) {
         if (!sUserManager.exists(userId)) return null;
@@ -4506,7 +4551,7 @@
             if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
                 return null;
             }
-            if (filterAppAccessLPr(ps, filterCallingUid, userId)) {
+            if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
                 return null;
             }
             if (ps.pkg == null) {
@@ -4549,7 +4594,7 @@
         }
 
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // Normalize package name to handle renamed packages and static libs
             packageName = resolveInternalPackageNameLPr(packageName,
                     PackageManager.VERSION_CODE_HIGHEST);
@@ -4564,7 +4609,7 @@
                 if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) {
                     return null;
                 }
-                if (filterAppAccessLPr(ps, filterCallingUid, userId)) {
+                if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
                     return null;
                 }
                 // Note: isEnabledLP() does not apply here - always return info
@@ -4587,7 +4632,7 @@
         return null;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private String normalizePackageNameLPr(String packageName) {
         String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
         return normalizedPackageName != null ? normalizedPackageName : packageName;
@@ -4742,7 +4787,7 @@
         List<VersionedPackage> packagesToDelete = null;
         final long now = System.currentTimeMillis();
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final int[] allUsers = sUserManager.getUserIds();
             final int libCount = mSharedLibraries.size();
             for (int i = 0; i < libCount; i++) {
@@ -4980,14 +5025,15 @@
                     false /* requireFullPermission */, false /* checkShell */, "get activity info");
         }
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             PackageParser.Activity a = mComponentResolver.getActivity(component);
 
             if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
             if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
-                if (filterAppAccessLPr(ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
+                if (shouldFilterApplicationLocked(
+                        ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
                     return null;
                 }
                 return PackageParser.generateActivityInfo(
@@ -5020,7 +5066,7 @@
     @Override
     public boolean activitySupportsIntent(ComponentName component, Intent intent,
             String resolvedType) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (component.equals(mResolveComponentName)) {
                 // The resolver supports EVERYTHING!
                 return true;
@@ -5035,7 +5081,8 @@
             if (ps == null) {
                 return false;
             }
-            if (filterAppAccessLPr(ps, callingUid, component, TYPE_ACTIVITY, callingUserId)) {
+            if (shouldFilterApplicationLocked(
+                    ps, callingUid, component, TYPE_ACTIVITY, callingUserId)) {
                 return false;
             }
             for (int i=0; i<a.intents.size(); i++) {
@@ -5055,14 +5102,15 @@
         flags = updateFlagsForComponent(flags, userId, component);
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /* requireFullPermission */, false /* checkShell */, "get receiver info");
-        synchronized (mPackages) {
+        synchronized (mLock) {
             PackageParser.Activity a = mComponentResolver.getReceiver(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
                 TAG, "getReceiverInfo " + component + ": " + a);
             if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
-                if (filterAppAccessLPr(ps, callingUid, component, TYPE_RECEIVER, userId)) {
+                if (shouldFilterApplicationLocked(
+                        ps, callingUid, component, TYPE_RECEIVER, userId)) {
                     return null;
                 }
                 return PackageParser.generateActivityInfo(
@@ -5096,7 +5144,7 @@
                 || mContext.checkCallingOrSelfPermission(
                         Manifest.permission.ACCESS_SHARED_LIBRARIES) == PERMISSION_GRANTED;
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             List<SharedLibraryInfo> result = null;
 
             final int libCount = mSharedLibraries.size();
@@ -5165,7 +5213,7 @@
             return null;
         }
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             List<SharedLibraryInfo> result = null;
 
             int libraryCount = mSharedLibraries.size();
@@ -5214,7 +5262,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private List<VersionedPackage> getPackagesUsingSharedLibraryLPr(
             SharedLibraryInfo libInfo, int flags, int userId) {
         List<VersionedPackage> versionedPackages = null;
@@ -5269,14 +5317,15 @@
         flags = updateFlagsForComponent(flags, userId, component);
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /* requireFullPermission */, false /* checkShell */, "get service info");
-        synchronized (mPackages) {
+        synchronized (mLock) {
             PackageParser.Service s = mComponentResolver.getService(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
                 TAG, "getServiceInfo " + component + ": " + s);
             if (s != null && mSettings.isEnabledAndMatchLPr(s.info, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
-                if (filterAppAccessLPr(ps, callingUid, component, TYPE_SERVICE, userId)) {
+                if (shouldFilterApplicationLocked(
+                        ps, callingUid, component, TYPE_SERVICE, userId)) {
                     return null;
                 }
                 return PackageParser.generateServiceInfo(
@@ -5293,14 +5342,15 @@
         flags = updateFlagsForComponent(flags, userId, component);
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /* requireFullPermission */, false /* checkShell */, "get provider info");
-        synchronized (mPackages) {
+        synchronized (mLock) {
             PackageParser.Provider p = mComponentResolver.getProvider(component);
             if (DEBUG_PACKAGE_INFO) Log.v(
                 TAG, "getProviderInfo " + component + ": " + p);
             if (p != null && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {
                 PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                 if (ps == null) return null;
-                if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) {
+                if (shouldFilterApplicationLocked(
+                        ps, callingUid, component, TYPE_PROVIDER, userId)) {
                     return null;
                 }
                 return PackageParser.generateProviderInfo(
@@ -5323,7 +5373,7 @@
     @Override
     public String[] getSystemSharedLibraryNames() {
         // allow instant applications
-        synchronized (mPackages) {
+        synchronized (mLock) {
             Set<String> libs = null;
             final int libCount = mSharedLibraries.size();
             for (int i = 0; i < libCount; i++) {
@@ -5367,7 +5417,7 @@
     @Override
     public @NonNull String getServicesSystemSharedLibraryPackageName() {
         // allow instant applications
-        synchronized (mPackages) {
+        synchronized (mLock) {
             return mServicesSystemSharedLibraryPackageName;
         }
     }
@@ -5375,12 +5425,12 @@
     @Override
     public @NonNull String getSharedSystemSharedLibraryPackageName() {
         // allow instant applications
-        synchronized (mPackages) {
+        synchronized (mLock) {
             return mSharedSystemSharedLibraryPackageName;
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) {
         for (int i = userList.length - 1; i >= 0; --i) {
             final int userId = userList[i];
@@ -5413,7 +5463,7 @@
         if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
             return null;
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (sequenceNumber >= mChangedPackagesSequenceNumber) {
                 return null;
             }
@@ -5487,13 +5537,13 @@
 
     @Override
     public String getPermissionControllerPackageName() {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             return mRequiredPermissionControllerPackage;
         }
     }
 
     String getPackageInstallerPackageName() {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             return mRequiredInstallerPackage;
         }
     }
@@ -5561,7 +5611,7 @@
 
     @Override
     public int checkSignatures(String pkg1, String pkg2) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageParser.Package p1 = mPackages.get(pkg1);
             final PackageParser.Package p2 = mPackages.get(pkg2);
             if (p1 == null || p1.mExtras == null
@@ -5572,8 +5622,8 @@
             final int callingUserId = UserHandle.getUserId(callingUid);
             final PackageSetting ps1 = (PackageSetting) p1.mExtras;
             final PackageSetting ps2 = (PackageSetting) p2.mExtras;
-            if (filterAppAccessLPr(ps1, callingUid, callingUserId)
-                    || filterAppAccessLPr(ps2, callingUid, callingUserId)) {
+            if (shouldFilterApplicationLocked(ps1, callingUid, callingUserId)
+                    || shouldFilterApplicationLocked(ps2, callingUid, callingUserId)) {
                 return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
             }
             return compareSignatures(p1.mSigningDetails.signatures, p2.mSigningDetails.signatures);
@@ -5589,7 +5639,7 @@
         final int appId1 = UserHandle.getAppId(uid1);
         final int appId2 = UserHandle.getAppId(uid2);
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             Signature[] s1;
             Signature[] s2;
             Object obj = mSettings.getSettingLPr(appId1);
@@ -5601,7 +5651,7 @@
                     s1 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
                 } else if (obj instanceof PackageSetting) {
                     final PackageSetting ps = (PackageSetting) obj;
-                    if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+                    if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
                         return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
                     }
                     s1 = ps.signatures.mSigningDetails.signatures;
@@ -5620,7 +5670,7 @@
                     s2 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
                 } else if (obj instanceof PackageSetting) {
                     final PackageSetting ps = (PackageSetting) obj;
-                    if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+                    if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
                         return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
                     }
                     s2 = ps.signatures.mSigningDetails.signatures;
@@ -5638,7 +5688,7 @@
     public boolean hasSigningCertificate(
             String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageParser.Package p = mPackages.get(packageName);
             if (p == null || p.mExtras == null) {
                 return false;
@@ -5646,7 +5696,7 @@
             final int callingUid = Binder.getCallingUid();
             final int callingUserId = UserHandle.getUserId(callingUid);
             final PackageSetting ps = (PackageSetting) p.mExtras;
-            if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+            if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
                 return false;
             }
             switch (type) {
@@ -5668,7 +5718,7 @@
         // Map to base uids.
         final int appId = UserHandle.getAppId(uid);
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageParser.SigningDetails signingDetails;
             final Object obj = mSettings.getSettingLPr(appId);
             if (obj != null) {
@@ -5680,7 +5730,7 @@
                     signingDetails = ((SharedUserSetting)obj).signatures.mSigningDetails;
                 } else if (obj instanceof PackageSetting) {
                     final PackageSetting ps = (PackageSetting) obj;
-                    if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+                    if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
                         return false;
                     }
                     signingDetails = ps.signatures.mSigningDetails;
@@ -5726,7 +5776,7 @@
     public List<String> getAllPackages() {
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (canViewInstantApps(callingUid, callingUserId)) {
                 return new ArrayList<>(mPackages.keySet());
             }
@@ -5775,7 +5825,7 @@
         final int userId = UserHandle.getUserId(uid);
         final int appId = UserHandle.getAppId(uid);
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final Object obj = mSettings.getSettingLPr(appId);
             if (obj instanceof SharedUserSetting) {
                 if (isCallerInstantApp) {
@@ -5797,7 +5847,8 @@
                 return res;
             } else if (obj instanceof PackageSetting) {
                 final PackageSetting ps = (PackageSetting) obj;
-                if (ps.getInstalled(userId) && !filterAppAccessLPr(ps, callingUid, userId)) {
+                if (ps.getInstalled(userId)
+                        && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
                     return new String[]{ps.name};
                 }
             }
@@ -5812,14 +5863,15 @@
             return null;
         }
         final int appId = UserHandle.getAppId(uid);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final Object obj = mSettings.getSettingLPr(appId);
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.name + ":" + sus.userId;
             } else if (obj instanceof PackageSetting) {
                 final PackageSetting ps = (PackageSetting) obj;
-                if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+                if (shouldFilterApplicationLocked(
+                        ps, callingUid, UserHandle.getUserId(callingUid))) {
                     return null;
                 }
                 return ps.name;
@@ -5838,7 +5890,7 @@
             return null;
         }
         final String[] names = new String[uids.length];
-        synchronized (mPackages) {
+        synchronized (mLock) {
             for (int i = uids.length - 1; i >= 0; i--) {
                 final int appId = UserHandle.getAppId(uids[i]);
                 final Object obj = mSettings.getSettingLPr(appId);
@@ -5847,7 +5899,8 @@
                     names[i] = "shared:" + sus.name;
                 } else if (obj instanceof PackageSetting) {
                     final PackageSetting ps = (PackageSetting) obj;
-                    if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+                    if (shouldFilterApplicationLocked(
+                            ps, callingUid, UserHandle.getUserId(callingUid))) {
                         names[i] = null;
                     } else {
                         names[i] = ps.name;
@@ -5869,7 +5922,7 @@
             return -1;
         }
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             SharedUserSetting suid;
             try {
                 suid = mSettings.getSharedUserLPw(sharedUserName, 0, 0, false);
@@ -5890,14 +5943,15 @@
             return 0;
         }
         final int appId = UserHandle.getAppId(uid);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final Object obj = mSettings.getSettingLPr(appId);
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.pkgFlags;
             } else if (obj instanceof PackageSetting) {
                 final PackageSetting ps = (PackageSetting) obj;
-                if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+                if (shouldFilterApplicationLocked(
+                        ps, callingUid, UserHandle.getUserId(callingUid))) {
                     return 0;
                 }
                 return ps.pkgFlags;
@@ -5913,14 +5967,15 @@
             return 0;
         }
         final int appId = UserHandle.getAppId(uid);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final Object obj = mSettings.getSettingLPr(appId);
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.pkgPrivateFlags;
             } else if (obj instanceof PackageSetting) {
                 final PackageSetting ps = (PackageSetting) obj;
-                if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+                if (shouldFilterApplicationLocked(
+                        ps, callingUid, UserHandle.getUserId(callingUid))) {
                     return 0;
                 }
                 return ps.pkgPrivateFlags;
@@ -5936,7 +5991,7 @@
         }
         final int appId = UserHandle.getAppId(uid);
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final Object obj = mSettings.getSettingLPr(appId);
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
@@ -6017,7 +6072,7 @@
                 0, userId, intent, callingUid, false /*includeInstantApps*/);
         final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
                 userId);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             return findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false,
                     userId);
         }
@@ -6104,7 +6159,7 @@
         }
         // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
         // Or if there's already an ephemeral app installed that handles the action
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
             for (int n = 0; n < count; n++) {
                 final ResolveInfo info = resolvedActivities.get(n);
@@ -6246,7 +6301,7 @@
         return true;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType,
             int flags, List<ResolveInfo> query, boolean debug, int userId) {
         final int N = query.size();
@@ -6316,7 +6371,7 @@
     ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
             List<ResolveInfo> query, int priority, boolean always,
             boolean removeMatches, boolean debug, int userId) {
-        if (Thread.holdsLock(mPackages)) {
+        if (Thread.holdsLock(mLock)) {
             Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
                     + " is holding mPackages", new Throwable());
         }
@@ -6331,7 +6386,7 @@
                 flags, userId, intent, callingUid, false /*includeInstantApps*/);
         intent = updateIntentForResolve(intent);
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // Try to find a matching persistent preferred activity.
             ResolveInfo pri = findPersistentPreferredActivityLP(intent, resolvedType, flags, query,
                     debug, userId);
@@ -6535,7 +6590,7 @@
             // cross-profile app linking works only towards the parent.
             final int callingUid = Binder.getCallingUid();
             final UserInfo parent = getProfileParent(sourceUserId);
-            synchronized(mPackages) {
+            synchronized (mLock) {
                 int flags = updateFlagsForResolve(0, parent.id, intent, callingUid,
                         false /*includeInstantApps*/);
                 CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr(
@@ -6582,7 +6637,7 @@
      * instant, returns {@code null}.
      */
     private String getInstantAppPackageName(int callingUid) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // If the caller is an isolated app use the owner's uid for the lookup.
             if (Process.isIsolated(callingUid)) {
                 callingUid = mIsolatedOwners.get(callingUid);
@@ -6673,7 +6728,7 @@
         boolean sortResult = false;
         boolean addInstant = false;
         List<ResolveInfo> result;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (pkgName == null) {
                 List<CrossProfileIntentFilter> matchingFilters =
                         getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
@@ -7112,7 +7167,7 @@
         final ArrayList<ResolveInfo> neverList = new ArrayList<>();
         final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final int count = candidates.size();
             // First, try to use linked apps. Partition the candidates into four lists:
             // one for the final results, one for the "do not use ever", one for "undefined status"
@@ -7631,7 +7686,7 @@
         }
 
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             String pkgName = intent.getPackage();
             if (pkgName == null) {
                 final List<ResolveInfo> result =
@@ -7735,7 +7790,7 @@
         }
 
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             String pkgName = intent.getPackage();
             if (pkgName == null) {
                 return applyPostServiceResolutionFilter(
@@ -7853,7 +7908,7 @@
         }
 
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             String pkgName = intent.getPackage();
             if (pkgName == null) {
                 return applyPostContentProviderResolutionFilter(
@@ -7931,7 +7986,7 @@
                 "get installed packages");
 
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             ArrayList<PackageInfo> list;
             if (listUninstalled) {
                 list = new ArrayList<>(mSettings.mPackages.size());
@@ -7939,7 +7994,7 @@
                     if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
                         continue;
                     }
-                    if (filterAppAccessLPr(ps, callingUid, userId)) {
+                    if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                         continue;
                     }
                     final PackageInfo pi = generatePackageInfo(ps, flags, userId);
@@ -7954,7 +8009,7 @@
                     if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
                         continue;
                     }
-                    if (filterAppAccessLPr(ps, callingUid, userId)) {
+                    if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                         continue;
                     }
                     final PackageInfo pi = generatePackageInfo((PackageSetting)
@@ -8028,7 +8083,7 @@
         final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
 
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             ArrayList<PackageInfo> list = new ArrayList<>();
             boolean[] tmpBools = new boolean[permissions.length];
             if (listUninstalled) {
@@ -8074,7 +8129,7 @@
             "get installed application info");
 
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             ArrayList<ApplicationInfo> list;
             if (listUninstalled) {
                 list = new ArrayList<>(mSettings.mPackages.size());
@@ -8088,7 +8143,7 @@
                         if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
                             continue;
                         }
-                        if (filterAppAccessLPr(ps, callingUid, userId)) {
+                        if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                             continue;
                         }
                         ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags,
@@ -8114,7 +8169,7 @@
                         if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
                             continue;
                         }
-                        if (filterAppAccessLPr(ps, callingUid, userId)) {
+                        if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                             continue;
                         }
                         ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
@@ -8143,7 +8198,7 @@
         mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "getEphemeralApplications");
-        synchronized (mPackages) {
+        synchronized (mLock) {
             List<InstantAppInfo> instantApps = mInstantAppRegistry
                     .getInstantAppsLPr(userId);
             if (instantApps != null) {
@@ -8162,7 +8217,7 @@
             return false;
         }
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             int callingUid = Binder.getCallingUid();
             if (Process.isIsolated(callingUid)) {
                 callingUid = mIsolatedOwners.get(callingUid);
@@ -8194,7 +8249,7 @@
         if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
             return null;
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             return mInstantAppRegistry.getInstantAppCookieLPw(
                     packageName, userId);
         }
@@ -8212,7 +8267,7 @@
         if (!isCallerSameApp(packageName, Binder.getCallingUid())) {
             return false;
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             return mInstantAppRegistry.setInstantAppCookieLPw(
                     packageName, cookie, userId);
         }
@@ -8232,7 +8287,7 @@
                 true /* requireFullPermission */, false /* checkShell */,
                 "getInstantAppIcon");
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             return mInstantAppRegistry.getInstantAppIconLPw(
                     packageName, userId);
         }
@@ -8256,7 +8311,7 @@
         final ArrayList<ApplicationInfo> finalList = new ArrayList<>();
 
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final Iterator<PackageParser.Package> i = mPackages.values().iterator();
             final int userId = UserHandle.getCallingUserId();
             while (i.hasNext()) {
@@ -8302,11 +8357,11 @@
         if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) {
             return null;
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName);
             final ComponentName component =
                     new ComponentName(providerInfo.packageName, providerInfo.name);
-            if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) {
+            if (shouldFilterApplicationLocked(ps, callingUid, component, TYPE_PROVIDER, userId)) {
                 return null;
             }
             return providerInfo;
@@ -8337,7 +8392,7 @@
         final List<ProviderInfo> matchList =
                 mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId);
         final int listSize = (matchList == null ? 0 : matchList.size());
-        synchronized (mPackages) {
+        synchronized (mLock) {
             for (int i = 0; i < listSize; i++) {
                 final ProviderInfo providerInfo = matchList.get(i);
                 if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) {
@@ -8346,7 +8401,8 @@
                 final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName);
                 final ComponentName component =
                         new ComponentName(providerInfo.packageName, providerInfo.name);
-                if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) {
+                if (shouldFilterApplicationLocked(
+                        ps, callingUid, component, TYPE_PROVIDER, userId)) {
                     continue;
                 }
                 if (finalList == null) {
@@ -8367,12 +8423,13 @@
     @Override
     public InstrumentationInfo getInstrumentationInfo(ComponentName component, int flags) {
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final int callingUid = Binder.getCallingUid();
             final int callingUserId = UserHandle.getUserId(callingUid);
             final PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
             if (ps == null) return null;
-            if (filterAppAccessLPr(ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) {
+            if (shouldFilterApplicationLocked(
+                    ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) {
                 return null;
             }
             final PackageParser.Instrumentation i = mInstrumentation.get(component);
@@ -8386,7 +8443,7 @@
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
         final PackageSetting ps = mSettings.mPackages.get(targetPackage);
-        if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+        if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
             return ParceledListSlice.emptyList();
         }
         return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags));
@@ -8397,7 +8454,7 @@
         ArrayList<InstrumentationInfo> finalList = new ArrayList<>();
 
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final Iterator<PackageParser.Instrumentation> i = mInstrumentation.values().iterator();
             while (i.hasNext()) {
                 final PackageParser.Instrumentation p = i.next();
@@ -8562,7 +8619,7 @@
      *  Traces a package scan.
      *  @see #scanPackageLI(File, int, int, long, UserHandle)
      */
-    @GuardedBy({"mInstallLock", "mPackages"})
+    @GuardedBy({"mInstallLock", "mLock"})
     private PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
             int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
@@ -8577,7 +8634,7 @@
      *  Scans a package and returns the newly parsed package.
      *  Returns {@code null} in case of errors and the error code is stored in mLastScanError
      */
-    @GuardedBy({"mInstallLock", "mPackages"})
+    @GuardedBy({"mInstallLock", "mLock"})
     private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
             long currentTime, UserHandle user) throws PackageManagerException {
         if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
@@ -8609,7 +8666,7 @@
      *  Scans a package and returns the newly parsed package.
      *  @throws PackageManagerException on a parse error.
      */
-    @GuardedBy({"mInstallLock", "mPackages"})
+    @GuardedBy({"mInstallLock", "mLock"})
     private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg,
             final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user)
@@ -8705,7 +8762,7 @@
      * structures and the package is made available to the rest of the system.
      * <p>NOTE: The return value should be removed. It's the passed in package object.
      */
-    @GuardedBy({"mInstallLock", "mPackages"})
+    @GuardedBy({"mInstallLock", "mLock"})
     private PackageParser.Package addForInitLI(PackageParser.Package pkg,
             @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user)
@@ -8730,7 +8787,7 @@
         pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
         pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
             final String realPkgName = getRealPackageName(pkg, renamedPkgName);
             if (realPkgName != null) {
@@ -8791,7 +8848,8 @@
                             null /* originalPkgSetting */, null, parseFlags, scanFlags,
                             (pkg == mPlatformPackage), user);
                     applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
-                    final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L);
+                    final ScanResult scanResult =
+                            scanPackageOnlyLI(request, mInjector, mFactoryTest, -1L);
                     if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) {
                         scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting);
                     }
@@ -8810,7 +8868,7 @@
             // /data. Switch back to the application on /system.
             // It's safe to assume the application on /system will correctly scan. If not,
             // there won't be a working copy of the application.
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 // just remove the loaded entries from package lists
                 mPackages.remove(pkgSetting.name);
             }
@@ -8825,7 +8883,7 @@
                     pkgSetting.codePathString,
                     pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
             args.cleanUpResourcesLI();
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mSettings.enableSystemPackageLPw(pkgSetting.name);
             }
         }
@@ -8914,7 +8972,7 @@
         final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags
                 | SCAN_UPDATE_SIGNATURE, currentTime, user);
         if (scanResult.success) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 boolean appIdCreated = false;
                 try {
                     final String pkgName = scanResult.pkgSetting.name;
@@ -8940,7 +8998,7 @@
         }
 
         if (shouldHideSystemApp) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mSettings.disableSystemPackageLPw(pkg.packageName, true);
             }
         }
@@ -9011,7 +9069,7 @@
                 }
                 if (doTrim) {
                     final boolean dexOptDialogShown;
-                    synchronized (mPackages) {
+                    synchronized (mLock) {
                         dexOptDialogShown = mDexOptDialogShown;
                     }
                     if (!isFirstBoot() && dexOptDialogShown) {
@@ -9052,7 +9110,7 @@
         }
 
         List<PackageParser.Package> pkgs;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
         }
 
@@ -9180,7 +9238,7 @@
                                     numberOfPackagesVisited, numberOfPackagesToDexopt), true);
                 } catch (RemoteException e) {
                 }
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     mDexOptDialogShown = true;
                 }
             }
@@ -9233,7 +9291,7 @@
 
     @Override
     public void notifyPackageUse(String packageName, int reason) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final int callingUid = Binder.getCallingUid();
             final int callingUserId = UserHandle.getUserId(callingUid);
             if (getInstantAppPackageName(callingUid) != null) {
@@ -9249,7 +9307,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void notifyPackageUseLocked(String packageName, int reason) {
         final PackageParser.Package p = mPackages.get(packageName);
         if (p == null) {
@@ -9336,7 +9394,7 @@
     @Override
     public boolean compileLayouts(String packageName) {
         PackageParser.Package pkg;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             pkg = mPackages.get(packageName);
             if (pkg == null) {
                 return false;
@@ -9383,7 +9441,7 @@
     // if the package can now be considered up to date for the given filter.
     private int performDexOptInternal(DexoptOptions options) {
         PackageParser.Package p;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             p = mPackages.get(options.getPackageName());
             if (p == null) {
                 // Package could not be found. Report failure.
@@ -9404,7 +9462,7 @@
 
     public ArraySet<String> getOptimizablePackages() {
         ArraySet<String> pkgs = new ArraySet<>();
-        synchronized (mPackages) {
+        synchronized (mLock) {
             for (PackageParser.Package p : mPackages.values()) {
                 if (PackageDexOptimizer.canOptimizePackage(p)) {
                     pkgs.add(p.packageName);
@@ -9439,7 +9497,7 @@
                     options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
             for (SharedLibraryInfo info : deps) {
                 PackageParser.Package depPackage = null;
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     depPackage = mPackages.get(info.getPackageName());
                 }
                 if (depPackage != null) {
@@ -9527,7 +9585,7 @@
         List<SharedLibraryInfo> deps = findSharedLibraries(pkg);
         if (!deps.isEmpty()) {
             ArrayList<PackageParser.Package> retValue = new ArrayList<>();
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 for (SharedLibraryInfo info : deps) {
                     PackageParser.Package depPackage = mPackages.get(info.getPackageName());
                     if (depPackage != null) {
@@ -9591,7 +9649,7 @@
     @Nullable
     private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) {
         PackageSetting sharedLibPackage = null;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final SharedLibraryInfo latestSharedLibraVersionLPr =
                     getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg);
             if (latestSharedLibraVersionLPr != null) {
@@ -9609,7 +9667,7 @@
         PackageWatchdog.getInstance(mContext).writeNow();
 
         // This is the last chance to write out pending restriction settings
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
                 mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
                 for (int userId : mDirtyUsers) {
@@ -9623,7 +9681,7 @@
     @Override
     public void dumpProfiles(String packageName) {
         PackageParser.Package pkg;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             pkg = mPackages.get(packageName);
             if (pkg == null) {
                 throw new IllegalArgumentException("Unknown package: " + packageName);
@@ -9649,7 +9707,7 @@
         enforceSystemOrRoot("forceDexOpt");
 
         PackageParser.Package pkg;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             pkg = mPackages.get(packageName);
             if (pkg == null) {
                 throw new IllegalArgumentException("Unknown package: " + packageName);
@@ -9674,7 +9732,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) {
         if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
             Slog.w(TAG, "Unable to update from " + oldPkg.name
@@ -9725,7 +9783,7 @@
 
     private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
         final PackageSetting ps;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             ps = mSettings.mPackages.get(pkg.packageName);
         }
         for (int realUserId : resolveUserIds(userId)) {
@@ -9753,7 +9811,7 @@
 
     private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) {
         final PackageSetting ps;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             ps = mSettings.mPackages.get(pkg.packageName);
         }
         for (int realUserId : resolveUserIds(userId)) {
@@ -9820,7 +9878,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void applyDefiningSharedLibraryUpdateLocked(
             PackageParser.Package pkg, SharedLibraryInfo libInfo,
             BiConsumer<SharedLibraryInfo, SharedLibraryInfo> action) {
@@ -9849,7 +9907,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void addSharedLibraryLPr(PackageParser.Package pkg, Set<String> usesLibraryFiles,
             SharedLibraryInfo libInfo, PackageParser.Package changingLib) {
         if (libInfo.getPath() != null) {
@@ -9878,7 +9936,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void updateSharedLibrariesLocked(PackageParser.Package pkg,
             PackageParser.Package changingLib, Map<String, PackageParser.Package> availablePackages)
                     throws PackageManagerException {
@@ -9940,7 +9998,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
             @NonNull List<String> requestedLibraries,
             @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
@@ -10050,7 +10108,7 @@
         return false;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private ArrayList<PackageParser.Package> updateAllSharedLibrariesLocked(
             PackageParser.Package updatedPkg,
             Map<String, PackageParser.Package> availablePackages) {
@@ -10110,7 +10168,7 @@
         return resultList;
     }
 
-    @GuardedBy({"mInstallLock", "mPackages"})
+    @GuardedBy({"mInstallLock", "mLock"})
     private List<ScanResult> scanPackageTracedLI(PackageParser.Package pkg,
             final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user) throws PackageManagerException {
@@ -10151,7 +10209,8 @@
     }
 
     /** The result of a package scan. */
-    private static class ScanResult {
+    @VisibleForTesting
+    static class ScanResult {
         /** The request that initiated the scan that produced this result. */
         public final ScanRequest request;
         /** Whether or not the package scan was successful */
@@ -10190,7 +10249,8 @@
     }
 
     /** A package to be scanned */
-    private static class ScanRequest {
+    @VisibleForTesting
+    static class ScanRequest {
         /** The parsed package */
         @NonNull public final PackageParser.Package pkg;
         /** The package this package replaces */
@@ -10325,7 +10385,7 @@
                 // TODO(b/72378145) Fix this exemption. Force signature apps
                 // to whitelist their privileged permissions just like other
                 // priv-apps.
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
                     if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures,
                                 pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH)) {
@@ -10342,7 +10402,7 @@
     // the results / removing app data needs to be moved up a level to the callers of this
     // method. Also, we need to solve the problem of potentially creating a new shared user
     // setting. That can probably be done later and patch things up after the fact.
-    @GuardedBy({"mInstallLock", "mPackages"})
+    @GuardedBy({"mInstallLock", "mLock"})
     private ScanResult scanPackageNewLI(@NonNull PackageParser.Package pkg,
             final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user) throws PackageManagerException {
@@ -10363,7 +10423,7 @@
         }
 
         scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, pkg);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
             assertPackageIsValid(pkg, parseFlags, scanFlags);
 
@@ -10383,7 +10443,7 @@
                     pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
                     originalPkgSetting, realPkgName, parseFlags, scanFlags,
                     (pkg == mPlatformPackage), user);
-            return scanPackageOnlyLI(request, mFactoryTest, currentTime);
+            return scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime);
         }
     }
 
@@ -10424,7 +10484,7 @@
      * This needs to be fixed so, once we get to this point, no errors are
      * possible and the system is not left in an inconsistent state.
      */
-    @GuardedBy({"mPackages", "mInstallLock"})
+    @GuardedBy({"mLock", "mInstallLock"})
     private void commitReconciledScanResultLocked(@NonNull ReconciledPackage reconciledPkg) {
         final ScanResult result = reconciledPkg.scanResult;
         final ScanRequest request = result.request;
@@ -10512,7 +10572,7 @@
 
         if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
             if (oldPkgSetting != null) {
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     mSettings.mPackages.put(oldPkgSetting.name, oldPkgSetting);
                 }
             }
@@ -10553,7 +10613,7 @@
      * <p>An original package must be signed identically and it must have the same
      * shared user [if any].
      */
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private @Nullable PackageSetting getOriginalPackageLocked(@NonNull PackageParser.Package pkg,
             @Nullable String renamedPkgName) {
         if (!isPackageRenamed(pkg, renamedPkgName)) {
@@ -10609,15 +10669,21 @@
      * method potentially modifies a live {@link PackageSetting} object representing
      * the package being scanned. This will be resolved in the future.
      *
+     * @param injector injector for acquiring dependencies
      * @param request Information about the package to be scanned
      * @param isUnderFactoryTest Whether or not the device is under factory test
      * @param currentTime The current time, in millis
      * @return The results of the scan
      */
     @GuardedBy("mInstallLock")
-    private static @NonNull ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
+    @VisibleForTesting
+    @NonNull
+    static ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
+            Injector injector,
             boolean isUnderFactoryTest, long currentTime)
-                    throws PackageManagerException {
+            throws PackageManagerException {
+        final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
+        final UserManagerInternal userManager = injector.getUserManager();
         final PackageParser.Package pkg = request.pkg;
         PackageSetting pkgSetting = request.pkgSetting;
         final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
@@ -10723,7 +10789,7 @@
         if (!createNewPackage) {
             final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
             final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
-            setInstantAppForUser(pkgSetting, userId, instantApp, fullApp);
+            setInstantAppForUser(userManager, pkgSetting, userId, instantApp, fullApp);
         }
         // TODO(patb): see if we can do away with disabled check here.
         if (disabledPkgSetting != null
@@ -10769,7 +10835,7 @@
             if (needToDeriveAbi) {
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
                 final boolean extractNativeLibs = !pkg.isLibrary();
-                derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs);
+                packageAbiHelper.derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
                 // Some system apps still use directory structure for native libraries
@@ -10777,8 +10843,8 @@
                 // structure. Try to detect abi based on directory structure.
                 if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
                         pkg.applicationInfo.primaryCpuAbi == null) {
-                    setBundledAppAbisAndRoots(pkg, pkgSetting);
-                    setNativeLibraryPaths(pkg, sAppLib32InstallDir);
+                    packageAbiHelper.setBundledAppAbisAndRoots(pkg, pkgSetting);
+                    packageAbiHelper.setNativeLibraryPaths(pkg, sAppLib32InstallDir);
                 }
             } else {
                 // This is not a first boot or an upgrade, don't bother deriving the
@@ -10787,7 +10853,7 @@
                 pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings;
                 pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings;
 
-                setNativeLibraryPaths(pkg, sAppLib32InstallDir);
+                packageAbiHelper.setNativeLibraryPaths(pkg, sAppLib32InstallDir);
 
                 if (DEBUG_ABI_SELECTION) {
                     Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
@@ -10808,7 +10874,7 @@
             // ABIs we've determined above. For non-moves, the path will be updated based on the
             // ABIs we determined during compilation, but the path will depend on the final
             // package path (after the rename away from the stage path).
-            setNativeLibraryPaths(pkg, sAppLib32InstallDir);
+            packageAbiHelper.setNativeLibraryPaths(pkg, sAppLib32InstallDir);
         }
 
         // This is a special case for the "system" package, where the ABI is
@@ -10862,8 +10928,8 @@
             // We also do this *before* we perform dexopt on this package, so that
             // we can avoid redundant dexopts, and also to make sure we've got the
             // code and package path correct.
-            changedAbiCodePath =
-                    adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg);
+            changedAbiCodePath = packageAbiHelper.adjustCpuAbisForSharedUser(
+                            pkgSetting.sharedUser.packages, pkg);
         }
 
         if (isUnderFactoryTest && pkg.requestedPermissions.contains(
@@ -11142,7 +11208,7 @@
         final KeySetManagerService ksms = mSettings.mKeySetManagerService;
         ksms.assertScannedPackageValid(pkg);
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // The special "android" package can only be defined once
             if (pkg.packageName.equals("android")) {
                 if (mAndroidApplication != null) {
@@ -11458,7 +11524,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private boolean addBuiltInSharedLibraryLocked(String path, String name) {
         if (nonStaticSharedLibExistsLocked(name)) {
             return false;
@@ -11473,7 +11539,7 @@
         return true;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private boolean nonStaticSharedLibExistsLocked(String name) {
         return sharedLibExists(name, SharedLibraryInfo.VERSION_UNDEFINED, mSharedLibraries);
     }
@@ -11487,7 +11553,7 @@
         return false;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void commitSharedLibraryInfoLocked(SharedLibraryInfo libraryInfo) {
         final String name = libraryInfo.getName();
         LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
@@ -11537,7 +11603,7 @@
         }
 
         if (pkg.packageName.equals("android")) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
                     // Set up information for our fall-back user intent resolution activity.
                     mPlatformPackage = pkg;
@@ -11575,7 +11641,7 @@
 
         ArrayList<PackageParser.Package> clientLibPkgs = null;
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (!ArrayUtils.isEmpty(reconciledPkg.allowedSharedLibraryInfos)) {
                 for (SharedLibraryInfo info : reconciledPkg.allowedSharedLibraryInfos) {
                     commitSharedLibraryInfoLocked(info);
@@ -11621,7 +11687,7 @@
         // writer
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // We don't expect installation to fail beyond this point
 
             // Add the new setting to mSettings
@@ -11634,6 +11700,7 @@
             ksms.addScannedPackageLPw(pkg);
 
             mComponentResolver.addAllComponents(pkg, chatty);
+            mAppsFilter.addPackage(pkg, mPackages);
 
             // Don't allow ephemeral applications to define new permissions groups.
             if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
@@ -11713,266 +11780,8 @@
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
     }
 
-    /**
-     * Derive the ABI of a non-system package located at {@code scanFile}. This information
-     * is derived purely on the basis of the contents of {@code scanFile} and
-     * {@code cpuAbiOverride}.
-     *
-     * If {@code extractLibs} is true, native libraries are extracted from the app if required.
-     */
-    private static void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride,
-            boolean extractLibs)
-                    throws PackageManagerException {
-        // Give ourselves some initial paths; we'll come back for another
-        // pass once we've determined ABI below.
-        setNativeLibraryPaths(pkg, sAppLib32InstallDir);
-
-        // We shouldn't attempt to extract libs from system app when it was not updated.
-        if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
-            extractLibs = false;
-        }
-
-        final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir;
-        final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa;
-
-        NativeLibraryHelper.Handle handle = null;
-        try {
-            handle = NativeLibraryHelper.Handle.create(pkg);
-            // TODO(multiArch): This can be null for apps that didn't go through the
-            // usual installation process. We can calculate it again, like we
-            // do during install time.
-            //
-            // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally
-            // unnecessary.
-            final File nativeLibraryRoot = new File(nativeLibraryRootStr);
-
-            // Null out the abis so that they can be recalculated.
-            pkg.applicationInfo.primaryCpuAbi = null;
-            pkg.applicationInfo.secondaryCpuAbi = null;
-            if (isMultiArch(pkg.applicationInfo)) {
-                // Warn if we've set an abiOverride for multi-lib packages..
-                // By definition, we need to copy both 32 and 64 bit libraries for
-                // such packages.
-                if (pkg.cpuAbiOverride != null
-                        && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) {
-                    Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
-                }
-
-                int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
-                int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
-                if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
-                    if (extractLibs) {
-                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
-                        abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
-                                nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
-                                useIsaSpecificSubdirs);
-                    } else {
-                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
-                        abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
-                    }
-                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-                }
-
-                // Shared library native code should be in the APK zip aligned
-                if (abi32 >= 0 && pkg.isLibrary() && extractLibs) {
-                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                            "Shared library native lib extraction not supported");
-                }
-
-                maybeThrowExceptionForMultiArchCopy(
-                        "Error unpackaging 32 bit native libs for multiarch app.", abi32);
-
-                if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
-                    if (extractLibs) {
-                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
-                        abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
-                                nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
-                                useIsaSpecificSubdirs);
-                    } else {
-                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
-                        abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
-                    }
-                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-                }
-
-                maybeThrowExceptionForMultiArchCopy(
-                        "Error unpackaging 64 bit native libs for multiarch app.", abi64);
-
-                if (abi64 >= 0) {
-                    // Shared library native libs should be in the APK zip aligned
-                    if (extractLibs && pkg.isLibrary()) {
-                        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                                "Shared library native lib extraction not supported");
-                    }
-                    pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
-                }
-
-                if (abi32 >= 0) {
-                    final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
-                    if (abi64 >= 0) {
-                        if (pkg.use32bitAbi) {
-                            pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
-                            pkg.applicationInfo.primaryCpuAbi = abi;
-                        } else {
-                            pkg.applicationInfo.secondaryCpuAbi = abi;
-                        }
-                    } else {
-                        pkg.applicationInfo.primaryCpuAbi = abi;
-                    }
-                }
-            } else {
-                String[] abiList = (cpuAbiOverride != null) ?
-                        new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
-
-                // Enable gross and lame hacks for apps that are built with old
-                // SDK tools. We must scan their APKs for renderscript bitcode and
-                // not launch them if it's present. Don't bother checking on devices
-                // that don't have 64 bit support.
-                boolean needsRenderScriptOverride = false;
-                if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
-                        NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
-                    abiList = Build.SUPPORTED_32_BIT_ABIS;
-                    needsRenderScriptOverride = true;
-                }
-
-                final int copyRet;
-                if (extractLibs) {
-                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
-                    copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
-                            nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
-                } else {
-                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
-                    copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
-                }
-                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
-                if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
-                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                            "Error unpackaging native libs for app, errorCode=" + copyRet);
-                }
-
-                if (copyRet >= 0) {
-                    // Shared libraries that have native libs must be multi-architecture
-                    if (pkg.isLibrary()) {
-                        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
-                                "Shared library with native libs must be multiarch");
-                    }
-                    pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
-                } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {
-                    pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
-                } else if (needsRenderScriptOverride) {
-                    pkg.applicationInfo.primaryCpuAbi = abiList[0];
-                }
-            }
-        } catch (IOException ioe) {
-            Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
-        } finally {
-            IoUtils.closeQuietly(handle);
-        }
-
-        // Now that we've calculated the ABIs and determined if it's an internal app,
-        // we will go ahead and populate the nativeLibraryPath.
-        setNativeLibraryPaths(pkg, sAppLib32InstallDir);
-    }
-
-    /**
-     * Adjusts ABIs for a set of packages belonging to a shared user so that they all match.
-     * i.e, so that all packages can be run inside a single process if required.
-     *
-     * Optionally, callers can pass in a parsed package via {@code newPackage} in which case
-     * this function will either try and make the ABI for all packages in {@code packagesForUser}
-     * match {@code scannedPackage} or will update the ABI of {@code scannedPackage} to match
-     * the ABI selected for {@code packagesForUser}. This variant is used when installing or
-     * updating a package that belongs to a shared user.
-     *
-     * NOTE: We currently only match for the primary CPU abi string. Matching the secondary
-     * adds unnecessary complexity.
-     */
-    private static @Nullable List<String> adjustCpuAbisForSharedUserLPw(
-            Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) {
-        List<String> changedAbiCodePath = null;
-        String requiredInstructionSet = null;
-        if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
-            requiredInstructionSet = VMRuntime.getInstructionSet(
-                     scannedPackage.applicationInfo.primaryCpuAbi);
-        }
-
-        PackageSetting requirer = null;
-        for (PackageSetting ps : packagesForUser) {
-            // If packagesForUser contains scannedPackage, we skip it. This will happen
-            // when scannedPackage is an update of an existing package. Without this check,
-            // we will never be able to change the ABI of any package belonging to a shared
-            // user, even if it's compatible with other packages.
-            if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) {
-                if (ps.primaryCpuAbiString == null) {
-                    continue;
-                }
-
-                final String instructionSet = VMRuntime.getInstructionSet(ps.primaryCpuAbiString);
-                if (requiredInstructionSet != null && !instructionSet.equals(requiredInstructionSet)) {
-                    // We have a mismatch between instruction sets (say arm vs arm64) warn about
-                    // this but there's not much we can do.
-                    String errorMessage = "Instruction set mismatch, "
-                            + ((requirer == null) ? "[caller]" : requirer)
-                            + " requires " + requiredInstructionSet + " whereas " + ps
-                            + " requires " + instructionSet;
-                    Slog.w(TAG, errorMessage);
-                }
-
-                if (requiredInstructionSet == null) {
-                    requiredInstructionSet = instructionSet;
-                    requirer = ps;
-                }
-            }
-        }
-
-        if (requiredInstructionSet != null) {
-            String adjustedAbi;
-            if (requirer != null) {
-                // requirer != null implies that either scannedPackage was null or that scannedPackage
-                // did not require an ABI, in which case we have to adjust scannedPackage to match
-                // the ABI of the set (which is the same as requirer's ABI)
-                adjustedAbi = requirer.primaryCpuAbiString;
-                if (scannedPackage != null) {
-                    scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi;
-                }
-            } else {
-                // requirer == null implies that we're updating all ABIs in the set to
-                // match scannedPackage.
-                adjustedAbi =  scannedPackage.applicationInfo.primaryCpuAbi;
-            }
-
-            for (PackageSetting ps : packagesForUser) {
-                if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) {
-                    if (ps.primaryCpuAbiString != null) {
-                        continue;
-                    }
-
-                    ps.primaryCpuAbiString = adjustedAbi;
-                    if (ps.pkg != null && ps.pkg.applicationInfo != null &&
-                            !TextUtils.equals(adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) {
-                        ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
-                        if (DEBUG_ABI_SELECTION) {
-                            Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi
-                                    + " (requirer="
-                                    + (requirer != null ? requirer.pkg : "null")
-                                    + ", scannedPackage="
-                                    + (scannedPackage != null ? scannedPackage : "null")
-                                    + ")");
-                        }
-                        if (changedAbiCodePath == null) {
-                            changedAbiCodePath = new ArrayList<>();
-                        }
-                        changedAbiCodePath.add(ps.codePathString);
-                    }
-                }
-            }
-        }
-        return changedAbiCodePath;
-    }
-
     private void setUpCustomResolverActivity(PackageParser.Package pkg) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             mResolverReplaced = true;
             // Set up information for custom user intent resolution activity.
             mResolveActivity.applicationInfo = pkg.applicationInfo;
@@ -12022,207 +11831,6 @@
                 | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
     }
 
-    private static String calculateBundledApkRoot(final String codePathString) {
-        final File codePath = new File(codePathString);
-        final File codeRoot;
-        if (FileUtils.contains(Environment.getRootDirectory(), codePath)) {
-            codeRoot = Environment.getRootDirectory();
-        } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) {
-            codeRoot = Environment.getOemDirectory();
-        } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) {
-            codeRoot = Environment.getVendorDirectory();
-        } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
-            codeRoot = Environment.getOdmDirectory();
-        } else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) {
-            codeRoot = Environment.getProductDirectory();
-        } else if (FileUtils.contains(Environment.getSystemExtDirectory(), codePath)) {
-            codeRoot = Environment.getSystemExtDirectory();
-        } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
-            codeRoot = Environment.getOdmDirectory();
-        } else {
-            // Unrecognized code path; take its top real segment as the apk root:
-            // e.g. /something/app/blah.apk => /something
-            try {
-                File f = codePath.getCanonicalFile();
-                File parent = f.getParentFile();    // non-null because codePath is a file
-                File tmp;
-                while ((tmp = parent.getParentFile()) != null) {
-                    f = parent;
-                    parent = tmp;
-                }
-                codeRoot = f;
-                Slog.w(TAG, "Unrecognized code path "
-                        + codePath + " - using " + codeRoot);
-            } catch (IOException e) {
-                // Can't canonicalize the code path -- shenanigans?
-                Slog.w(TAG, "Can't canonicalize code path " + codePath);
-                return Environment.getRootDirectory().getPath();
-            }
-        }
-        return codeRoot.getPath();
-    }
-
-    /**
-     * Derive and set the location of native libraries for the given package,
-     * which varies depending on where and how the package was installed.
-     */
-    private static void setNativeLibraryPaths(PackageParser.Package pkg, File appLib32InstallDir) {
-        final ApplicationInfo info = pkg.applicationInfo;
-        final String codePath = pkg.codePath;
-        final File codeFile = new File(codePath);
-        final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
-
-        info.nativeLibraryRootDir = null;
-        info.nativeLibraryRootRequiresIsa = false;
-        info.nativeLibraryDir = null;
-        info.secondaryNativeLibraryDir = null;
-
-        if (isApkFile(codeFile)) {
-            // Monolithic install
-            if (bundledApp) {
-                // If "/system/lib64/apkname" exists, assume that is the per-package
-                // native library directory to use; otherwise use "/system/lib/apkname".
-                final String apkRoot = calculateBundledApkRoot(info.sourceDir);
-                final boolean is64Bit = VMRuntime.is64BitInstructionSet(
-                        getPrimaryInstructionSet(info));
-
-                // This is a bundled system app so choose the path based on the ABI.
-                // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
-                // is just the default path.
-                final String apkName = deriveCodePathName(codePath);
-                final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
-                info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir,
-                        apkName).getAbsolutePath();
-
-                if (info.secondaryCpuAbi != null) {
-                    final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME;
-                    info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
-                            secondaryLibDir, apkName).getAbsolutePath();
-                }
-            } else {
-                final String apkName = deriveCodePathName(codePath);
-                info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
-                        .getAbsolutePath();
-            }
-
-            info.nativeLibraryRootRequiresIsa = false;
-            info.nativeLibraryDir = info.nativeLibraryRootDir;
-        } else {
-            // Cluster install
-            info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
-            info.nativeLibraryRootRequiresIsa = true;
-
-            info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
-                    getPrimaryInstructionSet(info)).getAbsolutePath();
-
-            if (info.secondaryCpuAbi != null) {
-                info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,
-                        VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath();
-            }
-        }
-    }
-
-    /**
-     * Calculate the abis and roots for a bundled app. These can uniquely
-     * be determined from the contents of the system partition, i.e whether
-     * it contains 64 or 32 bit shared libraries etc. We do not validate any
-     * of this information, and instead assume that the system was built
-     * sensibly.
-     */
-    private static void setBundledAppAbisAndRoots(PackageParser.Package pkg,
-                                           PackageSetting pkgSetting) {
-        final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
-
-        // If "/system/lib64/apkname" exists, assume that is the per-package
-        // native library directory to use; otherwise use "/system/lib/apkname".
-        final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir);
-        setBundledAppAbi(pkg, apkRoot, apkName);
-        // pkgSetting might be null during rescan following uninstall of updates
-        // to a bundled app, so accommodate that possibility.  The settings in
-        // that case will be established later from the parsed package.
-        //
-        // If the settings aren't null, sync them up with what we've just derived.
-        // note that apkRoot isn't stored in the package settings.
-        if (pkgSetting != null) {
-            pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
-            pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
-        }
-    }
-
-    /**
-     * Deduces the ABI of a bundled app and sets the relevant fields on the
-     * parsed pkg object.
-     *
-     * @param apkRoot the root of the installed apk, something like {@code /system} or {@code /oem}
-     *        under which system libraries are installed.
-     * @param apkName the name of the installed package.
-     */
-    private static void setBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) {
-        final File codeFile = new File(pkg.codePath);
-
-        final boolean has64BitLibs;
-        final boolean has32BitLibs;
-        if (isApkFile(codeFile)) {
-            // Monolithic install
-            has64BitLibs = (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists();
-            has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists();
-        } else {
-            // Cluster install
-            final File rootDir = new File(codeFile, LIB_DIR_NAME);
-            if (!ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS)
-                    && !TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) {
-                final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]);
-                has64BitLibs = (new File(rootDir, isa)).exists();
-            } else {
-                has64BitLibs = false;
-            }
-            if (!ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS)
-                    && !TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) {
-                final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]);
-                has32BitLibs = (new File(rootDir, isa)).exists();
-            } else {
-                has32BitLibs = false;
-            }
-        }
-
-        if (has64BitLibs && !has32BitLibs) {
-            // The package has 64 bit libs, but not 32 bit libs. Its primary
-            // ABI should be 64 bit. We can safely assume here that the bundled
-            // native libraries correspond to the most preferred ABI in the list.
-
-            pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
-            pkg.applicationInfo.secondaryCpuAbi = null;
-        } else if (has32BitLibs && !has64BitLibs) {
-            // The package has 32 bit libs but not 64 bit libs. Its primary
-            // ABI should be 32 bit.
-
-            pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
-            pkg.applicationInfo.secondaryCpuAbi = null;
-        } else if (has32BitLibs && has64BitLibs) {
-            // The application has both 64 and 32 bit bundled libraries. We check
-            // here that the app declares multiArch support, and warn if it doesn't.
-            //
-            // We will be lenient here and record both ABIs. The primary will be the
-            // ABI that's higher on the list, i.e, a device that's configured to prefer
-            // 64 bit apps will see a 64 bit primary ABI,
-
-            if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) {
-                Slog.e(TAG, "Package " + pkg + " has multiple bundled libs, but is not multiarch.");
-            }
-
-            if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) {
-                pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
-                pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
-            } else {
-                pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
-                pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0];
-            }
-        } else {
-            pkg.applicationInfo.primaryCpuAbi = null;
-            pkg.applicationInfo.secondaryCpuAbi = null;
-        }
-    }
-
     private void killApplication(String pkgName, int appId, String reason) {
         killApplication(pkgName, appId, UserHandle.USER_ALL, reason);
     }
@@ -12271,7 +11879,7 @@
         }
 
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageParser.Package removedPackage = mPackages.remove(packageName);
             if (removedPackage != null) {
                 cleanPackageDataStructuresLILPw(removedPackage, chatty);
@@ -12286,7 +11894,7 @@
         }
 
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // Remove the parent package
             mPackages.remove(pkg.applicationInfo.packageName);
             cleanPackageDataStructuresLILPw(pkg, chatty);
@@ -12303,7 +11911,7 @@
 
     void cleanPackageDataStructuresLILPw(PackageParser.Package pkg, boolean chatty) {
         mComponentResolver.removeAllComponents(pkg, chatty);
-
+        mAppsFilter.removePackage(pkg.packageName);
         mPermissionManager.removeAllPermissions(pkg, chatty);
 
         final int instrumentationSize = pkg.instrumentation.size();
@@ -12395,7 +12003,7 @@
     @Override
     public void notifyPackageAdded(String packageName, int uid) {
         final PackageListObserver[] observers;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (mPackageListObservers.size() == 0) {
                 return;
             }
@@ -12411,7 +12019,7 @@
     @Override
     public void notifyPackageChanged(String packageName, int uid) {
         final PackageListObserver[] observers;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (mPackageListObservers.size() == 0) {
                 return;
             }
@@ -12433,7 +12041,7 @@
     @Override
     public void notifyPackageRemoved(String packageName, int uid) {
         final PackageListObserver[] observers;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (mPackageListObservers.size() == 0) {
                 return;
             }
@@ -12682,12 +12290,12 @@
             boolean sendAdded = false;
             boolean sendRemoved = false;
             // writer
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 pkgSetting = mSettings.mPackages.get(packageName);
                 if (pkgSetting == null) {
                     return false;
                 }
-                if (filterAppAccessLPr(pkgSetting, callingUid, userId)) {
+                if (shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
                     return false;
                 }
                 // Do not allow "android" is being disabled
@@ -12743,7 +12351,7 @@
         final int callingUid = Binder.getCallingUid();
         PackageManagerServiceUtils
                 .enforceSystemOrPhoneCaller("setSystemAppHiddenUntilInstalled", callingUid);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
             if (pkgSetting == null || !pkgSetting.isSystem()) {
                 return;
@@ -12768,7 +12376,7 @@
         final int callingUid = Binder.getCallingUid();
         PackageManagerServiceUtils
                 .enforceSystemOrPhoneCaller("setSystemAppInstallState", callingUid);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
             // The target app should always be in system
             if (pkgSetting == null || !pkgSetting.isSystem()) {
@@ -12857,12 +12465,12 @@
         long callingId = Binder.clearCallingIdentity();
         try {
             // writer
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 ps = mSettings.mPackages.get(packageName);
                 if (ps == null) {
                     return true;
                 }
-                if (filterAppAccessLPr(ps, callingUid, userId)) {
+                if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                     return true;
                 }
                 return ps.getHidden(userId);
@@ -12918,7 +12526,7 @@
                     (installFlags & PackageManager.INSTALL_FULL_APP) != 0;
 
             // writer
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 pkgSetting = mSettings.mPackages.get(packageName);
                 if (pkgSetting == null) {
                     return PackageManager.INSTALL_FAILED_INVALID_URI;
@@ -12948,7 +12556,8 @@
                     // upgrade app from instant to full; we don't allow app downgrade
                     installed = true;
                 }
-                setInstantAppForUser(pkgSetting, userId, instantApp, fullApp);
+                setInstantAppForUser(
+                        getUserManagerInternal(), pkgSetting, userId, instantApp, fullApp);
             }
 
             if (installed) {
@@ -12966,7 +12575,7 @@
                     }
                 }
                 sendPackageAddedForUser(packageName, pkgSetting, userId);
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     updateSequenceNumberLP(pkgSetting, new int[]{ userId });
                 }
                 // start async restore with no post-install since we finish install here
@@ -12996,8 +12605,8 @@
         }
     }
 
-    static void setInstantAppForUser(PackageSetting pkgSetting, int userId,
-            boolean instantApp, boolean fullApp) {
+    static void setInstantAppForUser(UserManagerInternal userManager, PackageSetting pkgSetting,
+            int userId, boolean instantApp, boolean fullApp) {
         // no state specified; do nothing
         if (!instantApp && !fullApp) {
             return;
@@ -13009,7 +12618,7 @@
                 pkgSetting.setInstantApp(false /*instantApp*/, userId);
             }
         } else {
-            for (int currentUserId : sUserManager.getUserIds()) {
+            for (int currentUserId : userManager.getUserIds()) {
                 if (instantApp && !pkgSetting.getInstantApp(currentUserId)) {
                     pkgSetting.setInstantApp(true /*instantApp*/, currentUserId);
                 } else if (fullApp && pkgSetting.getInstantApp(currentUserId)) {
@@ -13051,9 +12660,10 @@
         for (int i = 0; i < packageNames.length; i++) {
             final String packageName = packageNames[i];
             final PackageSetting pkgSetting;
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 pkgSetting = mSettings.mPackages.get(packageName);
-                if (pkgSetting == null || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
+                if (pkgSetting == null
+                        || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
                     Slog.w(TAG, "Could not find package setting for package: " + packageName
                             + ". Skipping...");
                     unactionedPackages.add(packageName);
@@ -13064,7 +12674,7 @@
                 unactionedPackages.add(packageName);
                 continue;
             }
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final int oldDistractionFlags = pkgSetting.getDistractionFlags(userId);
                 if (restrictionFlags != oldDistractionFlags) {
                     pkgSetting.setDistractionFlags(restrictionFlags, userId);
@@ -13079,7 +12689,7 @@
                     new String[changedPackagesList.size()]);
             sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId,
                     restrictionFlags);
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 scheduleWritePackageRestrictionsLocked(userId);
             }
         }
@@ -13143,9 +12753,10 @@
                 continue;
             }
             final PackageSetting pkgSetting;
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 pkgSetting = mSettings.mPackages.get(packageName);
-                if (pkgSetting == null || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
+                if (pkgSetting == null
+                        || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
                     Slog.w(TAG, "Could not find package setting for package: " + packageName
                             + ". Skipping suspending/un-suspending.");
                     unactionedPackages.add(packageName);
@@ -13156,7 +12767,7 @@
                 unactionedPackages.add(packageName);
                 continue;
             }
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
                         launcherExtras, userId);
             }
@@ -13170,7 +12781,7 @@
             sendPackagesSuspendedForUser(
                     changedPackages, changedUids.toArray(), userId, suspended, launcherExtras);
             sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, appExtras, userId);
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 scheduleWritePackageRestrictionsLocked(userId);
             }
         }
@@ -13184,9 +12795,9 @@
             throw new SecurityException("Calling package " + packageName
                     + " does not belong to calling uid " + callingUid);
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
+            if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
                 throw new IllegalArgumentException("Unknown target package: " + packageName);
             }
             final PackageUserState packageUserState = ps.readUserState(userId);
@@ -13236,9 +12847,9 @@
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "isPackageSuspendedForUser for user " + userId);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
+            if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
                 throw new IllegalArgumentException("Unknown target package: " + packageName);
             }
             return ps.getSuspended(userId);
@@ -13284,7 +12895,7 @@
     private void unsuspendForSuspendingPackages(Predicate<String> packagePredicate, int userId) {
         final List<String> affectedPackages = new ArrayList<>();
         final IntArray affectedUids = new IntArray();
-        synchronized (mPackages) {
+        synchronized (mLock) {
             for (PackageSetting ps : mSettings.mPackages.values()) {
                 final PackageUserState pus = ps.readUserState(userId);
                 if (pus.suspended && packagePredicate.test(pus.suspendingPackage)) {
@@ -13379,7 +12990,7 @@
                             + "\": required for permissions management");
                     continue;
                 }
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
                         Slog.w(TAG, "Cannot suspend package \"" + packageName
                                 + "\": protected package");
@@ -13537,7 +13148,7 @@
     }
 
     private int getUidForVerifier(VerifierInfo verifierInfo) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageParser.Package pkg = mPackages.get(verifierInfo.packageName);
             if (pkg == null) {
                 return -1;
@@ -13703,10 +13314,11 @@
         if (getInstantAppPackageName(callingUid) != null) {
             return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             if (ps == null
-                    || filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+                    || shouldFilterApplicationLocked(
+                    ps, callingUid, UserHandle.getUserId(callingUid))) {
                 return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
             }
             return mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
@@ -13719,9 +13331,10 @@
                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
 
         boolean result = false;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (filterAppAccessLPr(ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
+            if (shouldFilterApplicationLocked(
+                    ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
                 return false;
             }
             result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId);
@@ -13739,9 +13352,9 @@
         if (getInstantAppPackageName(callingUid) != null) {
             return ParceledListSlice.emptyList();
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+            if (shouldFilterApplicationLocked(ps, callingUid, UserHandle.getUserId(callingUid))) {
                 return ParceledListSlice.emptyList();
             }
             return new ParceledListSlice<>(mSettings.getIntentFilterVerificationsLPr(packageName));
@@ -13755,7 +13368,7 @@
         }
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             PackageParser.Package pkg = mPackages.get(packageName);
             if (pkg == null || pkg.activities == null) {
                 return ParceledListSlice.emptyList();
@@ -13764,7 +13377,7 @@
                 return ParceledListSlice.emptyList();
             }
             final PackageSetting ps = (PackageSetting) pkg.mExtras;
-            if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+            if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
                 return ParceledListSlice.emptyList();
             }
             final int count = pkg.activities.size();
@@ -13797,10 +13410,10 @@
             return;
         }
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage);
             if (targetPackageSetting == null
-                    || filterAppAccessLPr(
+                    || shouldFilterApplicationLocked(
                             targetPackageSetting, callingUid, UserHandle.getUserId(callingUid))) {
                 throw new IllegalArgumentException("Unknown target package: " + targetPackage);
             }
@@ -13879,12 +13492,13 @@
         }
         mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
                 callerPackageName);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             PackageSetting ps = mSettings.mPackages.get(packageName);
             if (ps == null) {
                 throw new IllegalArgumentException("Unknown target package " + packageName);
             }
-            if (filterAppAccessLPr(ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
+            if (shouldFilterApplicationLocked(
+                    ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
                 throw new IllegalArgumentException("Unknown target package " + packageName);
             }
             if (!Objects.equals(callerPackageName, ps.installerPackageName)) {
@@ -14424,7 +14038,7 @@
             String packageName = pkgLite.packageName;
             int installLocation = pkgLite.installLocation;
             // reader
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 // Currently installed package which the new package is attempting to replace or
                 // null if no such package is installed.
                 PackageParser.Package installedPkg = mPackages.get(packageName);
@@ -14763,7 +14377,7 @@
                     mPendingEnableRollback.append(enableRollbackToken, this);
 
                     final int[] installedUsers;
-                    synchronized (mPackages) {
+                    synchronized (mLock) {
                         PackageSetting ps = mSettings.getPackageLPr(pkgLite.packageName);
                         if (ps != null) {
                             installedUsers = ps.queryInstalledUsers(sUserManager.getUserIds(),
@@ -15201,16 +14815,6 @@
         }
     }
 
-    private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws
-            PackageManagerException {
-        if (copyRet < 0) {
-            if (copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
-                    copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
-                throw new PackageManagerException(copyRet, message);
-            }
-        }
-    }
-
     /**
      * Logic to handle movement of existing installed applications.
      */
@@ -15337,26 +14941,6 @@
         return result;
     }
 
-    // Utility method that returns the relative package path with respect
-    // to the installation directory. Like say for /data/data/com.test-1.apk
-    // string com.test-1 is returned.
-    static String deriveCodePathName(String codePath) {
-        if (codePath == null) {
-            return null;
-        }
-        final File codeFile = new File(codePath);
-        final String name = codeFile.getName();
-        if (codeFile.isDirectory()) {
-            return name;
-        } else if (name.endsWith(".apk") || name.endsWith(".tmp")) {
-            final int lastDot = name.lastIndexOf('.');
-            return name.substring(0, lastDot);
-        } else {
-            Slog.w(TAG, "Odd, " + codePath + " doesn't look like an APK");
-            return null;
-        }
-    }
-
     static class PackageInstalledInfo {
         String name;
         int uid;
@@ -15464,7 +15048,7 @@
             final int childCount = (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
             for (int i = 0; i < childCount; i++) {
                 PackageSetting childPs = null;
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     childPs = mSettings.getPackageLPr(ps.childPackageNames.get(i));
                 }
                 if (childPs != null) {
@@ -15475,7 +15059,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void enableSystemPackageLPw(PackageParser.Package pkg) {
         // Enable the parent package
         mSettings.enableSystemPackageLPw(pkg.packageName);
@@ -15487,7 +15071,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private boolean disableSystemPackageLPw(PackageParser.Package oldPkg,
             PackageParser.Package newPkg) {
         // Disable the parent package (parent always replaced)
@@ -15502,7 +15086,7 @@
         return disabled;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void setInstallerPackageNameLPw(PackageParser.Package pkg,
             String installerPackageName) {
         // Enable the parent package
@@ -15539,7 +15123,7 @@
         final String pkgName = pkg.packageName;
 
         if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.codePath);
-        synchronized (mPackages) {
+        synchronized (mLock) {
 // NOTE: This changes slightly to include UPDATE_PERMISSIONS_ALL regardless of the size of pkg.permissions
             mPermissionManager.updatePermissions(pkg.packageName, pkg);
             // For system-bundled packages, we assume that installing an upgraded version
@@ -15631,7 +15215,7 @@
         }
     }
 
-    @GuardedBy({"mInstallLock", "mPackages"})
+    @GuardedBy({"mInstallLock", "mLock"})
     private void installPackagesTracedLI(List<InstallRequest> requests) {
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
@@ -15774,7 +15358,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private static Map<String, ReconciledPackage> reconcilePackagesLocked(
             final ReconcileRequest request, KeySetManagerService ksms)
             throws ReconcileFailure {
@@ -16085,7 +15669,7 @@
         return true;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void commitPackagesLocked(final CommitRequest request) {
         // TODO: remove any expected failures from this method; this should only be able to fail due
         //       to unavoidable errors (I/O, etc.)
@@ -16151,8 +15735,9 @@
                     }
                 } else {
                     try {
+                        // Settings will be written during the call to updateSettingsLI().
                         executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName,
-                                true, request.mAllUsers, true, pkg);
+                                true, request.mAllUsers, false, pkg);
                     } catch (SystemDeleteException e) {
                         if (Build.IS_ENG) {
                             throw new RuntimeException("Unexpected failure", e);
@@ -16288,7 +15873,8 @@
                 final PrepareResult prepareResult;
                 try {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
-                    prepareResult = preparePackageLI(request.args, request.installResult);
+                    prepareResult =
+                            preparePackageLI(request.args, request.installResult);
                 } catch (PrepareFailure prepareFailure) {
                     request.installResult.setError(prepareFailure.error,
                             prepareFailure.getMessage());
@@ -16342,7 +15928,7 @@
                     Collections.unmodifiableMap(mPackages), versionInfos,
                     lastStaticSharedLibSettings);
             CommitRequest commitRequest = null;
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 Map<String, ReconciledPackage> reconciledPackages;
                 try {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
@@ -16363,11 +15949,6 @@
                     commitPackagesLocked(commitRequest);
                     success = true;
                 } finally {
-                    for (PrepareResult result : prepareResults.values()) {
-                        if (result.freezer != null) {
-                            result.freezer.close();
-                        }
-                    }
                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                 }
             }
@@ -16649,7 +16230,7 @@
 
         // If we are installing a clustered package add results for the children
         if (pkg.childPackages != null) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final int childCount = pkg.childPackages.size();
                 for (int i = 0; i < childCount; i++) {
                     PackageParser.Package childPkg = pkg.childPackages.get(i);
@@ -16711,7 +16292,7 @@
         pp = null;
         boolean systemApp = false;
         boolean replace = false;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // Check if installing already existing package
             if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                 String oldName = mSettings.getRenamedPackageLPr(pkgName);
@@ -16812,7 +16393,7 @@
                                 compareRecover);
                         // The new KeySets will be re-added later in the scanning process.
                         if (compatMatch) {
-                            synchronized (mPackages) {
+                            synchronized (mLock) {
                                 ksms.removeAppKeySetDataLPw(pkg.packageName);
                             }
                         }
@@ -16932,7 +16513,7 @@
             scanFlags |= SCAN_NO_DEX;
             scanFlags |= SCAN_MOVE;
 
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final PackageSetting ps = mSettings.mPackages.get(pkgName);
                 if (ps == null) {
                     res.setError(INSTALL_FAILED_INTERNAL_ERROR,
@@ -16953,7 +16534,7 @@
                 String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
                         args.abiOverride : pkg.cpuAbiOverride);
                 final boolean extractNativeLibs = !pkg.isLibrary();
-                derivePackageAbi(pkg, abiOverride, extractNativeLibs);
+                mInjector.getAbiHelper().derivePackageAbi(pkg, abiOverride, extractNativeLibs);
             } catch (PackageManagerException pme) {
                 Slog.e(TAG, "Error deriving application ABI", pme);
                 throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
@@ -17015,7 +16596,7 @@
                 final int[] allUsers;
                 final int[] installedUsers;
 
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     oldPackage = mPackages.get(pkgName11);
                     existingPackage = oldPackage;
                     if (DEBUG_INSTALL) {
@@ -17150,7 +16731,7 @@
                             }
                             childRemovedRes.isUpdate = false;
                             childRemovedRes.dataRemoved = true;
-                            synchronized (mPackages) {
+                            synchronized (mLock) {
                                 if (childPs != null) {
                                     childRemovedRes.origUsers = childPs.queryInstalledUsers(
                                             allUsers,
@@ -17222,7 +16803,7 @@
                 if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
 
                 // TODO(patb): MOVE TO RECONCILE
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     renamedPackage = mSettings.getRenamedPackageLPr(pkgName1);
                     if (renamedPackage != null) {
                         // A package with the same name is already installed, though
@@ -17274,7 +16855,7 @@
         // Collect files we care for fs-verity setup.
         ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
         if (legacyMode) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
                 if (ps != null && ps.isPrivileged()) {
                     fsverityCandidates.put(pkg.baseCodePath, null);
@@ -17401,7 +16982,7 @@
         int count = 0;
         final String packageName = pkg.packageName;
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // If this is a new install and we see that we've already run verification for this
             // package, we have nothing to do: it means the state was restored from backup.
             if (!replacing) {
@@ -17459,7 +17040,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private boolean needsNetworkVerificationLPr(ActivityIntentInfo filter) {
         final ComponentName cn  = filter.activity.getComponentName();
         final String packageName = cn.getPackageName();
@@ -17482,10 +17063,6 @@
         }
     }
 
-    private static boolean isMultiArch(ApplicationInfo info) {
-        return (info.flags & ApplicationInfo.FLAG_MULTIARCH) != 0;
-    }
-
     private static boolean isExternal(PackageParser.Package pkg) {
         return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
     }
@@ -17494,7 +17071,7 @@
         return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
     }
 
-    private static boolean isSystemApp(PackageParser.Package pkg) {
+    static boolean isSystemApp(PackageParser.Package pkg) {
         return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
 
@@ -17575,7 +17152,7 @@
         final String packageName = versionedPackage.getPackageName();
         final long versionCode = versionedPackage.getLongVersionCode();
         final String internalPackageName;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // Normalize package name to handle renamed packages and static libs
             internalPackageName = resolveInternalPackageNameLPr(packageName, versionCode);
         }
@@ -17688,7 +17265,7 @@
         return pkg.packageName;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private String resolveInternalPackageNameLPr(String packageName, long versionCode) {
         // Handle renamed packages
         String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
@@ -17899,7 +17476,7 @@
         int[] allUsers;
         /** enabled state of the uninstalled application */
         final int origEnabledState;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             uninstalledPs = mSettings.mPackages.get(packageName);
             if (uninstalledPs == null) {
                 Slog.w(TAG, "Not removing non-existent package " + packageName);
@@ -17965,7 +17542,7 @@
                 res = deletePackageLIF(packageName, UserHandle.of(removeUser), true, allUsers,
                         deleteFlags | PackageManager.DELETE_CHATTY, info, true, null);
             }
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 if (res) {
                     if (pkg != null) {
                         mInstantAppRegistry.onPackageUninstalledLPw(pkg, info.removedUsers);
@@ -17993,7 +17570,7 @@
             final PackageParser.Package stubPkg =
                     (disabledSystemPs == null) ? null : disabledSystemPs.pkg;
             if (stubPkg != null && stubPkg.isStub) {
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     // restore the enabled state of the stub; the state is overwritten when
                     // the stub is uninstalled
                     final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName);
@@ -18202,7 +17779,7 @@
         if (deletedPs != null) {
             if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
                 final SparseBooleanArray changedUsers = new SparseBooleanArray();
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
                     clearDefaultBrowserIfNeeded(packageName);
                     mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
@@ -18258,7 +17835,7 @@
                 }
             }
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // can downgrade to reader
             if (writeSettings) {
                 // Save settings now
@@ -18399,7 +17976,7 @@
                 outInfo, writeSettings, disabledPs.pkg);
 
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // NOTE: The system package always needs to be enabled; even if it's for
             // a compressed stub. If we don't, installing the system package fails
             // during scan [scanning checks the disabled packages]. We will reverse
@@ -18479,7 +18056,7 @@
         prepareAppDataAfterInstallLIF(pkg);
 
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
 
             // Propagate the permissions state as we do not want to drop on the floor
@@ -18528,7 +18105,7 @@
             boolean deleteCodeAndResources, int flags, int[] allUserHandles,
             PackageRemovedInfo outInfo, boolean writeSettings,
             PackageParser.Package replacingPackage) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (outInfo != null) {
                 outInfo.uid = ps.appId;
             }
@@ -18555,7 +18132,7 @@
         final int childCount = (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
         for (int i = 0; i < childCount; i++) {
             PackageSetting childPs;
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 childPs = mSettings.getPackageLPr(ps.childPackageNames.get(i));
             }
             if (childPs != null) {
@@ -18586,7 +18163,7 @@
             int userId) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.DELETE_PACKAGES, null);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // Cannot block uninstall of static shared libs as they are
             // considered a part of the using app (emulating static linking).
             // Also static libs are installed always on internal storage.
@@ -18604,9 +18181,9 @@
 
     @Override
     public boolean getBlockUninstallForUser(String packageName, int userId) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (ps == null || filterAppAccessLPr(ps, Binder.getCallingUid(), userId)) {
+            if (ps == null || shouldFilterApplicationLocked(ps, Binder.getCallingUid(), userId)) {
                 return false;
             }
             return mSettings.getBlockUninstallLPr(userId, packageName);
@@ -18616,7 +18193,7 @@
     @Override
     public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp) {
         enforceSystemOrRoot("setRequiredForSystemUser can only be run by the system or root");
-        synchronized (mPackages) {
+        synchronized (mLock) {
             PackageSetting ps = mSettings.mPackages.get(packageName);
             if (ps == null) {
                 Log.w(TAG, "Package doesn't exist: " + packageName);
@@ -18654,7 +18231,7 @@
      * deleted, {@code null} otherwise.
      */
     @Nullable
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private static DeletePackageAction mayDeletePackageLocked(
             PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs,
             @Nullable PackageSetting[] children, int flags, UserHandle user) {
@@ -18702,7 +18279,7 @@
             PackageRemovedInfo outInfo, boolean writeSettings,
             PackageParser.Package replacingPackage) {
         final DeletePackageAction action;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
             PackageSetting[] children = mSettings.getChildSettingsLPr(ps);
@@ -18754,7 +18331,7 @@
                     : UserHandle.USER_ALL;
 
             clearPackageStateForUserLIF(ps, removedUserId, outInfo, flags);
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 markPackageUninstalledForUserLPw(ps, user);
                 scheduleWritePackageRestrictionsLocked(user);
             }
@@ -18773,7 +18350,7 @@
             // they have set the special DELETE_SYSTEM_APP which requests different
             // semantics than normal for uninstalling system apps.
             final boolean clearPackageStateAndReturn;
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 markPackageUninstalledForUserLPw(ps, user);
                 if (!systemApp) {
                     // Do not uninstall the APK if an app should be cached
@@ -18803,7 +18380,7 @@
             }
             if (clearPackageStateAndReturn) {
                 clearPackageStateForUserLIF(ps, userId, outInfo, flags);
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     scheduleWritePackageRestrictionsLocked(user);
                 }
                 return;
@@ -18813,7 +18390,7 @@
         // If we are deleting a composite package for all users, keep track
         // of result for each child.
         if (ps.childPackageNames != null && outInfo != null) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final int childCount = ps.childPackageNames.size();
                 outInfo.removedChildPackages = new ArrayMap<>(childCount);
                 for (int i = 0; i < childCount; i++) {
@@ -18846,7 +18423,7 @@
         if (outInfo != null) {
             outInfo.removedForAllUsers = mPackages.get(ps.name) == null;
             if (outInfo.removedChildPackages != null) {
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     final int childCount = outInfo.removedChildPackages.size();
                     for (int i = 0; i < childCount; i++) {
                         PackageRemovedInfo childInfo = outInfo.removedChildPackages.valueAt(i);
@@ -18861,7 +18438,7 @@
             // child packages that appeared as they are declared in the system
             // app but were not declared in the update.
             if (systemApp) {
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     PackageSetting updatedPs = mSettings.getPackageLPr(ps.name);
                     final int childCount = (updatedPs.childPackageNames != null)
                             ? updatedPs.childPackageNames.size() : 0;
@@ -18889,7 +18466,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void markPackageUninstalledForUserLPw(PackageSetting ps, UserHandle user) {
         final int[] userIds = (user == null || user.getIdentifier() == UserHandle.USER_ALL)
                 ? sUserManager.getUserIds() : new int[] {user.getIdentifier()};
@@ -18923,7 +18500,7 @@
     private void clearPackageStateForUserLIF(PackageSetting ps, int userId,
             PackageRemovedInfo outInfo, int flags) {
         final PackageParser.Package pkg;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             pkg = mPackages.get(ps.name);
         }
 
@@ -18946,7 +18523,7 @@
             if (changedUsers.size() > 0) {
                 updateDefaultHomeNotLocked(changedUsers);
                 postPreferredActivityChangedBroadcast(nextUserId);
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     scheduleWritePackageRestrictionsLocked(nextUserId);
                 }
             }
@@ -18976,7 +18553,7 @@
         enforceSystemOrRoot("Only the system can clear all profile data");
 
         final PackageParser.Package pkg;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             pkg = mPackages.get(packageName);
         }
 
@@ -18998,7 +18575,8 @@
                 true /* requireFullPermission */, false /* checkShell */, "clear application data");
 
         final PackageSetting ps = mSettings.getPackageLPr(packageName);
-        final boolean filterApp = (ps != null && filterAppAccessLPr(ps, callingUid, userId));
+        final boolean filterApp =
+                (ps != null && shouldFilterApplicationLocked(ps, callingUid, userId));
         if (!filterApp && mProtectedPackages.isPackageDataProtected(userId, packageName)) {
             throw new SecurityException("Cannot clear data for a protected package: "
                     + packageName);
@@ -19014,7 +18592,7 @@
                         synchronized (mInstallLock) {
                             succeeded = clearApplicationUserDataLIF(packageName, userId);
                         }
-                        synchronized (mPackages) {
+                        synchronized (mLock) {
                             mInstantAppRegistry.deleteInstantApplicationMetadataLPw(
                                     packageName, userId);
                         }
@@ -19053,7 +18631,7 @@
 
         // Try finding details about the requested package
         PackageParser.Package pkg;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             pkg = mPackages.get(packageName);
             if (pkg == null) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
@@ -19148,7 +18726,7 @@
                 android.Manifest.permission.ACCESS_INSTANT_APPS);
 
         final PackageParser.Package pkg;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             pkg = mPackages.get(packageName);
         }
 
@@ -19191,7 +18769,7 @@
     @GuardedBy("mInstallLock")
     private boolean getPackageSizeInfoLI(String packageName, int userId, PackageStats stats) {
         final PackageSetting ps;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             ps = mSettings.mPackages.get(packageName);
             if (ps == null) {
                 Slog.w(TAG, "Failed to find settings for " + packageName);
@@ -19223,7 +18801,7 @@
         return true;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private int getUidTargetSdkVersionLockedLPr(int uid) {
         final int appId = UserHandle.getAppId(uid);
         final Object obj = mSettings.getSettingLPr(appId);
@@ -19248,7 +18826,7 @@
         return Build.VERSION_CODES.CUR_DEVELOPMENT;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private int getPackageTargetSdkVersionLockedLPr(String packageName) {
         final PackageParser.Package p = mPackages.get(packageName);
         if (p != null) {
@@ -19292,7 +18870,7 @@
                     + userId + ":");
             filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PreferredIntentResolver pir = mSettings.editPreferredActivitiesLPw(userId);
             pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
             scheduleWritePackageRestrictionsLocked(userId);
@@ -19344,7 +18922,7 @@
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
                 != PackageManager.PERMISSION_GRANTED) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 if (getUidTargetSdkVersionLockedLPr(callingUid)
                         < Build.VERSION_CODES.FROYO) {
                     Slog.w(TAG, "Ignoring replacePreferredActivity() from uid "
@@ -19356,7 +18934,7 @@
                     android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
         }
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
             if (pir != null) {
                 // Get all of the existing entries that exactly match this filter.
@@ -19420,7 +18998,7 @@
             return;
         }
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             PackageParser.Package pkg = mPackages.get(packageName);
             if (pkg == null || !isCallerSameApp(packageName, callingUid)) {
                 if (mContext.checkCallingOrSelfPermission(
@@ -19438,7 +19016,8 @@
             }
             final PackageSetting ps = mSettings.getPackageLPr(packageName);
             if (ps != null
-                    && filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+                    && shouldFilterApplicationLocked(
+                            ps, callingUid, UserHandle.getUserId(callingUid))) {
                 return;
             }
         }
@@ -19448,14 +19027,14 @@
         if (changedUsers.size() > 0) {
             updateDefaultHomeNotLocked(changedUsers);
             postPreferredActivityChangedBroadcast(callingUserId);
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 scheduleWritePackageRestrictionsLocked(callingUserId);
             }
         }
     }
 
     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void clearPackagePreferredActivitiesLPw(String packageName,
             @NonNull SparseBooleanArray outUserChanged, int userId) {
         ArrayList<PreferredActivity> removed = null;
@@ -19490,7 +19069,7 @@
     }
 
     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void clearIntentFilterVerificationsLPw(int userId) {
         final int packageCount = mPackages.size();
         for (int i = 0; i < packageCount; i++) {
@@ -19500,7 +19079,7 @@
     }
 
     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     void clearIntentFilterVerificationsLPw(String packageName, int userId) {
         if (userId == UserHandle.USER_ALL) {
             if (mSettings.removeIntentFilterVerificationLPw(packageName,
@@ -19544,7 +19123,7 @@
             if (changedUsers.size() > 0) {
                 postPreferredActivityChangedBroadcast(userId);
             }
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mSettings.applyDefaultPreferredAppsLPw(userId);
                 clearIntentFilterVerificationsLPw(userId);
                 primeDomainVerificationsLPw(userId);
@@ -19557,7 +19136,7 @@
             // callbacks to the package manager to request a default app reset.
             mPermissionManager.setDefaultBrowser(null, true, true, userId);
             resetNetworkPolicies(userId);
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 scheduleWritePackageRestrictionsLocked(userId);
             }
         } finally {
@@ -19574,7 +19153,7 @@
         int num = 0;
         final int userId = UserHandle.getCallingUserId();
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
             if (pir != null) {
                 final Iterator<PreferredActivity> it = pir.filterIterator();
@@ -19614,7 +19193,7 @@
                     + " for user " + userId + ":");
             filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
                     new PersistentPreferredActivity(filter, activity));
             scheduleWritePackageRestrictionsLocked(userId);
@@ -19632,7 +19211,7 @@
         }
         ArrayList<PersistentPreferredActivity> removed = null;
         boolean changed = false;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             for (int i=0; i<mSettings.mPersistentPreferredActivities.size(); i++) {
                 final int thisUserId = mSettings.mPersistentPreferredActivities.keyAt(i);
                 PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities
@@ -19663,7 +19242,7 @@
         if (changed) {
             updateDefaultHomeNotLocked(userId);
             postPreferredActivityChangedBroadcast(userId);
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 scheduleWritePackageRestrictionsLocked(userId);
             }
         }
@@ -19722,7 +19301,7 @@
             serializer.startDocument(null, true);
             serializer.startTag(null, TAG_PREFERRED_BACKUP);
 
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mSettings.writePreferredActivitiesLPr(serializer, userId, true);
             }
 
@@ -19750,7 +19329,7 @@
             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
             restoreFromXml(parser, userId, TAG_PREFERRED_BACKUP,
                     (readParser, readUserId) -> {
-                        synchronized (mPackages) {
+                        synchronized (mLock) {
                             mSettings.readPreferredActivitiesLPw(readParser, readUserId);
                         }
                         updateDefaultHomeNotLocked(readUserId);
@@ -19780,7 +19359,7 @@
             serializer.startDocument(null, true);
             serializer.startTag(null, TAG_DEFAULT_APPS);
 
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mSettings.writeDefaultAppsLPr(serializer, userId);
             }
 
@@ -19809,7 +19388,7 @@
             restoreFromXml(parser, userId, TAG_DEFAULT_APPS,
                     (parser1, userId1) -> {
                         final String defaultBrowser;
-                        synchronized (mPackages) {
+                        synchronized (mLock) {
                             mSettings.readDefaultAppsLPw(parser1, userId1);
                             defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1);
                         }
@@ -19838,7 +19417,7 @@
             serializer.startDocument(null, true);
             serializer.startTag(null, TAG_INTENT_FILTER_VERIFICATION);
 
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mSettings.writeAllDomainVerificationsLPr(serializer, userId);
             }
 
@@ -19866,7 +19445,7 @@
             parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
             restoreFromXml(parser, userId, TAG_INTENT_FILTER_VERIFICATION,
                     (parser1, userId1) -> {
-                        synchronized (mPackages) {
+                        synchronized (mLock) {
                             mSettings.readAllDomainVerificationsLPr(parser1, userId1);
                             mSettings.writeLPr();
                         }
@@ -19891,7 +19470,7 @@
             Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions");
             return;
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter,
                     ownerPackage, targetUserId, flags);
             CrossProfileIntentResolver resolver =
@@ -19919,7 +19498,7 @@
         enforceOwnerRights(ownerPackage, callingUid);
         PackageManagerServiceUtils.enforceShellRestriction(
                 UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             CrossProfileIntentResolver resolver =
                     mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
             ArraySet<CrossProfileIntentFilter> set =
@@ -20071,7 +19650,7 @@
 
     /** <b>must not hold {@link #mPackages}</b> */
     private void updateDefaultHomeNotLocked(SparseBooleanArray userIds) {
-        if (Thread.holdsLock(mPackages)) {
+        if (Thread.holdsLock(mLock)) {
             Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
                     + " is holding mPackages", new Throwable());
         }
@@ -20087,7 +19666,7 @@
      * @return Whether the ACTION_PREFERRED_ACTIVITY_CHANGED broadcast has been scheduled.
      */
     private boolean updateDefaultHomeNotLocked(int userId) {
-        if (Thread.holdsLock(mPackages)) {
+        if (Thread.holdsLock(mLock)) {
             Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
                     + " is holding mPackages", new Throwable());
         }
@@ -20276,7 +19855,7 @@
     @Override
     public void setUpdateAvailable(String packageName, boolean updateAvailable) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
             if (pkgSetting != null) {
                 pkgSetting.setUpdateAvailable(updateAvailable);
@@ -20321,7 +19900,7 @@
         ArrayList<String> components;
 
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             pkgSetting = mSettings.mPackages.get(packageName);
             if (pkgSetting == null) {
                 if (!isCallerInstantApp) {
@@ -20347,7 +19926,7 @@
         if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) {
             // Don't allow apps that don't have permission to modify other apps
             if (!allowedByPermission
-                    || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
+                    || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
                 throw new SecurityException(
                         "Attempt to change component state; "
                         + "pid=" + Binder.getCallingPid()
@@ -20368,7 +19947,7 @@
             throw new SecurityException("Cannot disable a system-generated component");
         }
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (callingUid == Process.SHELL_UID
                     && (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
                 // Shell can only change whole packages between ENABLED and DISABLED_USER states
@@ -20393,7 +19972,7 @@
         }
         if (className == null) {
             // We're dealing with an application/package level state change
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 if (pkgSetting.getEnabled(userId) == newState) {
                     // Nothing to do
                     return;
@@ -20417,11 +19996,11 @@
                 // Don't care about who enables an app.
                 callingPackage = null;
             }
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 pkgSetting.setEnabled(newState, userId, callingPackage);
             }
         } else {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 // We're dealing with a component level state change
                 // First, verify that this is a valid class name.
                 PackageParser.Package pkg = pkgSetting.pkg;
@@ -20458,7 +20037,7 @@
                 }
             }
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             scheduleWritePackageRestrictionsLocked(userId);
             updateSequenceNumberLP(pkgSetting, new int[] { userId });
             final long callingId = Binder.clearCallingIdentity();
@@ -20519,7 +20098,7 @@
         }
         mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/,
                 false /* checkShell */, "flushPackageRestrictions");
-        synchronized (mPackages) {
+        synchronized (mLock) {
             mSettings.writePackageRestrictionsLPr(userId);
             mDirtyUsers.remove(userId);
             if (mDirtyUsers.isEmpty()) {
@@ -20566,9 +20145,9 @@
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, true /* checkShell */, "stop package");
         // writer
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (!filterAppAccessLPr(ps, callingUid, userId)
+            if (!shouldFilterApplicationLocked(ps, callingUid, userId)
                     && mSettings.setPackageStoppedStateLPw(this, packageName, stopped,
                             allowedByPermission, callingUid, userId)) {
                 scheduleWritePackageRestrictionsLocked(userId);
@@ -20579,9 +20158,10 @@
     @Override
     public String getInstallerPackageName(String packageName) {
         final int callingUid = Binder.getCallingUid();
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+            if (shouldFilterApplicationLocked(
+                    ps, callingUid, UserHandle.getUserId(callingUid))) {
                 return null;
             }
             // InstallerPackageName for Apex is not stored in PackageManager
@@ -20594,7 +20174,7 @@
 
     public boolean isOrphaned(String packageName) {
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (!mPackages.containsKey(packageName)) {
                 return false;
             }
@@ -20609,8 +20189,9 @@
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /* requireFullPermission */, false /* checkShell */, "get enabled");
         // reader
-        synchronized (mPackages) {
-            if (filterAppAccessLPr(mSettings.getPackageLPr(packageName), callingUid, userId)) {
+        synchronized (mLock) {
+            if (shouldFilterApplicationLocked(
+                    mSettings.getPackageLPr(packageName), callingUid, userId)) {
                 return COMPONENT_ENABLED_STATE_DISABLED;
             }
             return mSettings.getApplicationEnabledSettingLPr(packageName, userId);
@@ -20624,8 +20205,9 @@
         int callingUid = Binder.getCallingUid();
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /*requireFullPermission*/, false /*checkShell*/, "getComponentEnabled");
-        synchronized (mPackages) {
-            if (filterAppAccessLPr(mSettings.getPackageLPr(component.getPackageName()), callingUid,
+        synchronized (mLock) {
+            if (shouldFilterApplicationLocked(
+                    mSettings.getPackageLPr(component.getPackageName()), callingUid,
                     component, TYPE_UNKNOWN, userId)) {
                 return COMPONENT_ENABLED_STATE_DISABLED;
             }
@@ -20686,7 +20268,7 @@
             Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled);
         }
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // Verify that all of the preferred activity components actually
             // exist.  It is possible for applications to be updated and at
             // that point remove a previously declared activity component that
@@ -20722,8 +20304,18 @@
         // Now that we've scanned all packages, and granted any default
         // permissions, ensure permissions are updated. Beware of dragons if you
         // try optimizing this.
-        synchronized (mPackages) {
+        synchronized (mLock) {
             mPermissionManager.updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false);
+
+            final PermissionPolicyInternal permissionPolicyInternal =
+                    LocalServices.getService(PermissionPolicyInternal.class);
+            permissionPolicyInternal.setOnInitializedCallback(userId -> {
+                // The SDK updated case is already handled when we run during the ctor.
+                synchronized (mPackages) {
+                    mPermissionManager.updateAllPermissions(
+                            StorageManager.UUID_PRIVATE_INTERNAL, false);
+                }
+            });
         }
 
         // Watch for external volumes that come and go over time
@@ -21017,7 +20609,7 @@
             } else if ("service-permissions".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS);
             } else if ("write".equals(cmd)) {
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     mSettings.writeLPr();
                     pw.println("Settings written.");
                     return;
@@ -21030,7 +20622,7 @@
         }
 
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
                 if (!checkin) {
                     if (dumpState.onTitlePrinted())
@@ -21409,7 +21001,7 @@
     private void dumpProto(FileDescriptor fd) {
         final ProtoOutputStream proto = new ProtoOutputStream(fd);
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final long requiredVerifierPackageToken =
                     proto.start(PackageServiceDumpProto.REQUIRED_VERIFIER_PACKAGE);
             proto.write(PackageServiceDumpProto.PackageShortProto.NAME, mRequiredVerifierPackage);
@@ -21481,7 +21073,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     @SuppressWarnings("resource")
     private void dumpDexoptStateLPr(PrintWriter pw, String packageName) {
         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
@@ -21510,7 +21102,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     @SuppressWarnings("resource")
     private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) {
         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
@@ -21660,7 +21252,7 @@
 
         final VersionInfo ver;
         final List<PackageSetting> packages;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             ver = mSettings.findOrCreateVersion(volumeUuid);
             packages = mSettings.getVolumePackagesLPr(volumeUuid);
         }
@@ -21710,7 +21302,7 @@
             }
         }
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion);
             if (sdkUpdated) {
                 logCriticalInfo(Log.INFO, "Platform changed from " + ver.sdkVersion + " to "
@@ -21746,7 +21338,7 @@
 
         final ArrayList<ApplicationInfo> unloaded = new ArrayList<>();
         synchronized (mInstallLock) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid);
             for (PackageSetting ps : packages) {
                 if (ps.pkg == null) continue;
@@ -21791,7 +21383,7 @@
 
     private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
             throws PackageManagerException {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             // Normalize package name to handle renamed packages
             packageName = normalizePackageNameLPr(packageName);
 
@@ -21810,7 +21402,7 @@
     }
 
     private List<String> collectAbsoluteCodePaths() {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             List<String> codePaths = new ArrayList<>();
             final int packageCount = mSettings.mPackages.size();
             for (int i = 0; i < packageCount; i++) {
@@ -21961,7 +21553,7 @@
         // Ensure that data directories are ready to roll for all packages
         // installed for this volume and user
         final List<PackageSetting> packages;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             packages = mSettings.getVolumePackagesLPr(volumeUuid);
         }
         int preparedCount = 0;
@@ -22003,7 +21595,7 @@
      */
     private void prepareAppDataAfterInstallLIF(PackageParser.Package pkg) {
         final PackageSetting ps;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             ps = mSettings.mPackages.get(pkg.packageName);
             mSettings.writeKernelMappingLPr(ps);
         }
@@ -22065,7 +21657,7 @@
         }
 
         final PackageSetting ps;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             ps = mSettings.mPackages.get(pkg.packageName);
         }
         final String volumeUuid = pkg.volumeUuid;
@@ -22126,7 +21718,7 @@
 
         if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
             // TODO: mark this structure as dirty so we persist it!
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 if (ps != null) {
                     ps.setCeDataInode(ceDataInode, userId);
                 }
@@ -22256,7 +21848,7 @@
         }
 
         public PackageFreezer(String packageName, int userId, String killReason) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mPackageName = packageName;
                 mWeFroze = mFrozenPackages.add(mPackageName);
 
@@ -22294,7 +21886,7 @@
         public void close() {
             mCloseGuard.close();
             if (mClosed.compareAndSet(false, true)) {
-                synchronized (mPackages) {
+                synchronized (mLock) {
                     if (mWeFroze) {
                         mFrozenPackages.remove(mPackageName);
                     }
@@ -22313,7 +21905,7 @@
      * Verify that given package is currently frozen.
      */
     private void checkPackageFrozen(String packageName) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             if (!mFrozenPackages.contains(packageName)) {
                 Slog.wtf(TAG, "Expected " + packageName + " to be frozen!", new Throwable());
             }
@@ -22357,12 +21949,12 @@
         final boolean isCurrentLocationExternal;
 
         // reader
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageParser.Package pkg = mPackages.get(packageName);
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             if (pkg == null
                     || ps == null
-                    || filterAppAccessLPr(ps, callingUid, user.getIdentifier())) {
+                    || shouldFilterApplicationLocked(ps, callingUid, user.getIdentifier())) {
                 throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
             }
             if (pkg.applicationInfo.isSystemApp()) {
@@ -22570,7 +22162,7 @@
      */
     private void logAppMovedStorage(String packageName, boolean isPreviousLocationExternal) {
         final PackageParser.Package pkg;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             pkg = mPackages.get(packageName);
         }
         if (pkg == null) {
@@ -22667,7 +22259,7 @@
 
     /** Called by UserManagerService */
     void cleanUpUser(UserManagerService userManager, int userHandle) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             mDirtyUsers.remove(userHandle);
             mUserNeedsBadging.delete(userHandle);
             mSettings.removeUserLPw(userHandle);
@@ -22682,7 +22274,7 @@
      * that are no longer in use by any other user.
      * @param userHandle the user being removed
      */
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void removeUnusedPackagesLPw(UserManagerService userManager, final int userHandle) {
         final boolean DEBUG_CLEAN_APKS = false;
         int [] users = userManager.getUserIds();
@@ -22733,7 +22325,7 @@
         synchronized (mInstallLock) {
             mSettings.createNewUserLI(this, mInstaller, userId, disallowedPackages);
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             scheduleWritePackageRestrictionsLocked(userId);
             scheduleWritePackageListLocked(userId);
             primeDomainVerificationsLPw(userId);
@@ -22750,7 +22342,7 @@
                 android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
                 "Only package verification agents can read the verifier device identity");
 
-        synchronized (mPackages) {
+        synchronized (mLock) {
             return mSettings.getVerifierDeviceIdentityLPw();
         }
     }
@@ -22812,14 +22404,15 @@
         if (packageName == null || alias == null) {
             return null;
         }
-        synchronized(mPackages) {
+        synchronized (mLock) {
             final PackageParser.Package pkg = mPackages.get(packageName);
             if (pkg == null) {
                 Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
             final PackageSetting ps = (PackageSetting) pkg.mExtras;
-            if (filterAppAccessLPr(ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
+            if (shouldFilterApplicationLocked(
+                    ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
                 Slog.w(TAG, "KeySet requested for filtered package: " + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
@@ -22833,7 +22426,7 @@
         if (packageName == null) {
             return null;
         }
-        synchronized(mPackages) {
+        synchronized (mLock) {
             final int callingUid = Binder.getCallingUid();
             final int callingUserId = UserHandle.getUserId(callingUid);
             final PackageParser.Package pkg = mPackages.get(packageName);
@@ -22842,7 +22435,7 @@
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
             final PackageSetting ps = (PackageSetting) pkg.mExtras;
-            if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+            if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
                 // filter and pretend the package doesn't exist
                 Slog.w(TAG, "KeySet requested for filtered package: " + packageName
                         + ", uid:" + callingUid);
@@ -22866,10 +22459,10 @@
         if (packageName == null || ks == null) {
             return false;
         }
-        synchronized(mPackages) {
+        synchronized (mLock) {
             final PackageParser.Package pkg = mPackages.get(packageName);
             if (pkg == null
-                    || filterAppAccessLPr((PackageSetting) pkg.mExtras, callingUid,
+                    || shouldFilterApplicationLocked((PackageSetting) pkg.mExtras, callingUid,
                             UserHandle.getUserId(callingUid))) {
                 Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
@@ -22892,10 +22485,10 @@
         if (packageName == null || ks == null) {
             return false;
         }
-        synchronized(mPackages) {
+        synchronized (mLock) {
             final PackageParser.Package pkg = mPackages.get(packageName);
             if (pkg == null
-                    || filterAppAccessLPr((PackageSetting) pkg.mExtras, callingUid,
+                    || shouldFilterApplicationLocked((PackageSetting) pkg.mExtras, callingUid,
                             UserHandle.getUserId(callingUid))) {
                 Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
                 throw new IllegalArgumentException("Unknown package: " + packageName);
@@ -22909,7 +22502,7 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void deletePackageIfUnusedLPr(final String packageName) {
         PackageSetting ps = mSettings.mPackages.get(packageName);
         if (ps == null) {
@@ -23181,7 +22774,7 @@
         }
 
         private SigningDetails getSigningDetails(@NonNull String packageName) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 PackageParser.Package p = mPackages.get(packageName);
                 if (p == null) {
                     return null;
@@ -23191,7 +22784,7 @@
         }
 
         private SigningDetails getSigningDetails(int uid) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final int appId = UserHandle.getAppId(uid);
                 final Object obj = mSettings.getSettingLPr(appId);
                 if (obj != null) {
@@ -23218,27 +22811,28 @@
 
         @Override
         public boolean filterAppAccess(PackageParser.Package pkg, int callingUid, int userId) {
-            synchronized (mPackages) {
-                return PackageManagerService.this
-                        .filterAppAccessLPr((PackageSetting) pkg.mExtras, callingUid, userId);
+            synchronized (mLock) {
+                return PackageManagerService.this.shouldFilterApplicationLocked(
+                        (PackageSetting) pkg.mExtras, callingUid, userId);
             }
         }
 
         @Override
         public boolean filterAppAccess(String packageName, int callingUid, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final PackageParser.Package pkg = mPackages.get(packageName);
                 if (pkg == null) {
                     return false;
                 }
                 return PackageManagerService.this
-                        .filterAppAccessLPr((PackageSetting) pkg.mExtras, callingUid, userId);
+                        .shouldFilterApplicationLocked(
+                                (PackageSetting) pkg.mExtras, callingUid, userId);
             }
         }
 
         @Override
         public PackageParser.Package getPackage(String packageName) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 packageName = resolveInternalPackageNameLPr(
                         packageName, PackageManager.VERSION_CODE_HIGHEST);
                 return mPackages.get(packageName);
@@ -23247,7 +22841,7 @@
 
         @Override
         public PackageParser.Package getPackage(int uid) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final String[] packageNames = getPackagesForUid(uid);
                 PackageParser.Package pkg = null;
                 final int numPackages = packageNames == null ? 0 : packageNames.length;
@@ -23260,7 +22854,7 @@
 
         @Override
         public PackageList getPackageList(PackageListObserver observer) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final int N = mPackages.size();
                 final ArrayList<String> list = new ArrayList<>(N);
                 for (int i = 0; i < N; i++) {
@@ -23276,14 +22870,14 @@
 
         @Override
         public void removePackageListObserver(PackageListObserver observer) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mPackageListObservers.remove(observer);
             }
         }
 
         @Override
         public PackageParser.Package getDisabledSystemPackage(String packageName) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
                 return (ps != null) ? ps.pkg : null;
             }
@@ -23336,7 +22930,7 @@
         public void setKeepUninstalledPackages(final List<String> packageList) {
             Preconditions.checkNotNull(packageList);
             List<String> removedFromList = null;
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 if (mKeepUninstalledPackages != null) {
                     final int packagesCount = mKeepUninstalledPackages.size();
                     for (int i = 0; i < packagesCount; i++) {
@@ -23362,7 +22956,7 @@
 
         @Override
         public boolean isPermissionsReviewRequired(String packageName, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final PackageParser.Package pkg = mPackages.get(packageName);
                 if (pkg == null) {
                     return false;
@@ -23382,7 +22976,7 @@
 
         @Override
         public Bundle getSuspendedPackageLauncherExtras(String packageName, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
                 PersistableBundle launcherExtras = null;
                 if (ps != null) {
@@ -23394,7 +22988,7 @@
 
         @Override
         public boolean isPackageSuspended(String packageName, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
                 return (ps != null) ? ps.getSuspended(userId) : false;
             }
@@ -23402,7 +22996,7 @@
 
         @Override
         public String getSuspendingPackage(String suspendedPackage, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
                 return (ps != null) ? ps.readUserState(userId).suspendingPackage : null;
             }
@@ -23410,7 +23004,7 @@
 
         @Override
         public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
                 return (ps != null) ? ps.readUserState(userId).dialogInfo : null;
             }
@@ -23418,7 +23012,7 @@
 
         @Override
         public int getDistractingPackageRestrictions(String packageName, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
                 return (ps != null) ? ps.getDistractionFlags(userId) : RESTRICTION_NONE;
             }
@@ -23505,7 +23099,7 @@
 
         @Override
         public boolean isPackageEphemeral(int userId, String packageName) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
                 return ps != null ? ps.getInstantApp(userId) : false;
             }
@@ -23513,21 +23107,21 @@
 
         @Override
         public boolean wasPackageEverLaunched(String packageName, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 return mSettings.wasPackageEverLaunchedLPr(packageName, userId);
             }
         }
 
         @Override
         public boolean isEnabledAndMatches(ComponentInfo info, int flags, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 return mSettings.isEnabledAndMatchLPr(info, flags, userId);
             }
         }
 
         @Override
         public boolean userNeedsBadging(int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 return PackageManagerService.this.userNeedsBadging(userId);
             }
         }
@@ -23549,7 +23143,7 @@
         @Override
         public void grantEphemeralAccess(int userId, Intent intent,
                 int targetAppId, int ephemeralAppId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
                         targetAppId, ephemeralAppId);
             }
@@ -23557,7 +23151,7 @@
 
         @Override
         public boolean isInstantAppInstallerComponent(ComponentName component) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 return mInstantAppInstallerActivity != null
                         && mInstantAppInstallerActivity.getComponentName().equals(component);
             }
@@ -23581,7 +23175,7 @@
 
         @Override
         public boolean isPackagePersistent(String packageName) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 PackageParser.Package pkg = mPackages.get(packageName);
                 return pkg != null
                         ? ((pkg.applicationInfo.flags&(ApplicationInfo.FLAG_SYSTEM
@@ -23593,7 +23187,7 @@
 
         @Override
         public boolean isLegacySystemApp(PackageParser.Package pkg) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final PackageSetting ps = (PackageSetting) pkg.mExtras;
                 return mPromoteSystemApps
                         && ps.isSystem()
@@ -23604,7 +23198,7 @@
         @Override
         public List<PackageInfo> getOverlayPackages(int userId) {
             final ArrayList<PackageInfo> overlayPackages = new ArrayList<PackageInfo>();
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 for (PackageParser.Package p : mPackages.values()) {
                     if (p.mOverlayTarget != null) {
                         PackageInfo pkg = generatePackageInfo((PackageSetting)p.mExtras, 0, userId);
@@ -23620,7 +23214,7 @@
         @Override
         public List<String> getTargetPackageNames(int userId) {
             List<String> targetPackages = new ArrayList<>();
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 for (PackageParser.Package p : mPackages.values()) {
                     if (p.mOverlayTarget == null) {
                         targetPackages.add(p.packageName);
@@ -23633,7 +23227,7 @@
         @Override
         public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName,
                 @Nullable List<String> overlayPackageNames) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 if (targetPackageName == null || mPackages.get(targetPackageName) == null) {
                     Slog.e(TAG, "failed to find package " + targetPackageName);
                     return false;
@@ -23680,28 +23274,28 @@
 
         @Override
         public void addIsolatedUid(int isolatedUid, int ownerUid) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mIsolatedOwners.put(isolatedUid, ownerUid);
             }
         }
 
         @Override
         public void removeIsolatedUid(int isolatedUid) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mIsolatedOwners.delete(isolatedUid);
             }
         }
 
         @Override
         public int getUidTargetSdkVersion(int uid) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 return getUidTargetSdkVersionLockedLPr(uid);
             }
         }
 
         @Override
         public int getPackageTargetSdkVersion(String packageName) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 return getPackageTargetSdkVersionLockedLPr(packageName);
             }
         }
@@ -23713,44 +23307,44 @@
 
         @Override
         public boolean canAccessComponent(int callingUid, ComponentName component, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
-                return ps != null && !PackageManagerService.this.filterAppAccessLPr(
+                return ps != null && !PackageManagerService.this.shouldFilterApplicationLocked(
                         ps, callingUid, component, TYPE_UNKNOWN, userId);
             }
         }
 
         @Override
         public boolean hasInstantApplicationMetadata(String packageName, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 return mInstantAppRegistry.hasInstantApplicationMetadataLPr(packageName, userId);
             }
         }
 
         @Override
         public void notifyPackageUse(String packageName, int reason) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 PackageManagerService.this.notifyPackageUseLocked(packageName, reason);
             }
         }
 
         @Override
         public SparseArray<String> getAppsWithSharedUserIds() {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 return getAppsWithSharedUserIdsLocked();
             }
         }
 
         @Override
         public String getSharedUserIdForPackage(String packageName) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 return getSharedUserIdForPackageLocked(packageName);
             }
         }
 
         @Override
         public String[] getPackagesForSharedUserId(String sharedUserId, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 return getPackagesForSharedUserIdLocked(sharedUserId, userId);
             }
         }
@@ -23779,7 +23373,7 @@
 
         @Override
         public ArraySet<String> getEnabledComponents(String packageName, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 PackageSetting setting = mSettings.getPackageLPr(packageName);
                 if (setting == null) {
                     return new ArraySet<>();
@@ -23790,7 +23384,7 @@
 
         @Override
         public ArraySet<String> getDisabledComponents(String packageName, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 PackageSetting setting = mSettings.getPackageLPr(packageName);
                 if (setting == null) {
                     return new ArraySet<>();
@@ -23802,7 +23396,7 @@
         @Override
         public @PackageManager.EnabledState int getApplicationEnabledState(
                 String packageName, int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 PackageSetting setting = mSettings.getPackageLPr(packageName);
                 if (setting == null) {
                     return COMPONENT_ENABLED_STATE_DEFAULT;
@@ -23822,7 +23416,7 @@
         @Override
         public boolean compileLayouts(String packageName) {
             PackageParser.Package pkg;
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 pkg = mPackages.get(packageName);
                 if (pkg == null) {
                     return false;
@@ -23839,7 +23433,7 @@
         @Nullable
         @Override
         public String removeLegacyDefaultBrowserPackageName(int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 return mSettings.removeDefaultBrowserPackageNameLPw(userId);
             }
         }
@@ -23892,7 +23486,7 @@
         @Override
         public void setRuntimePermissionsFingerPrint(@NonNull String fingerPrint,
                 @UserIdInt int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 mSettings.setRuntimePermissionsFingerPrintLPr(fingerPrint, userId);
             }
         }
@@ -23908,7 +23502,7 @@
 
         @Override
         public void writeSettings(boolean async) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 if (async) {
                     scheduleWriteSettingsLocked();
                 } else {
@@ -23919,7 +23513,7 @@
 
         @Override
         public void writePermissionSettings(int[] userIds, boolean async) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 for (int userId : userIds) {
                     mSettings.writeRuntimePermissionsForUserLPr(userId, !async);
                 }
@@ -23930,7 +23524,7 @@
         public int getTargetSdk(int uid) {
             int userId = UserHandle.getUserId(uid);
 
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 final Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
                 if (obj instanceof PackageSetting) {
                     final PackageSetting ps = (PackageSetting) obj;
@@ -23961,7 +23555,7 @@
         @Override
         public boolean isCallerInstallerOfRecord(
                 @NonNull PackageParser.Package pkg, int callingUid) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 if (pkg == null) {
                     return false;
                 }
@@ -23978,14 +23572,14 @@
 
         @Override
         public boolean areDefaultRuntimePermissionsGranted(int userId) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 return mSettings.areDefaultRuntimePermissionsGrantedLPr(userId);
             }
         }
 
         @Override
         public void setReadExternalStorageEnforced(boolean enforced) {
-            synchronized (mPackages) {
+            synchronized (mLock) {
                 if (mSettings.mReadExternalStorageEnforced != null
                         && mSettings.mReadExternalStorageEnforced == enforced) {
                     return;
@@ -23996,10 +23590,10 @@
         }
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private SparseArray<String> getAppsWithSharedUserIdsLocked() {
         final SparseArray<String> sharedUserIds = new SparseArray<>();
-        synchronized (mPackages) {
+        synchronized (mLock) {
             for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
                 sharedUserIds.put(UserHandle.getAppId(setting.userId), setting.name);
             }
@@ -24007,13 +23601,13 @@
         return sharedUserIds;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private String getSharedUserIdForPackageLocked(String packageName) {
         final PackageSetting ps = mSettings.mPackages.get(packageName);
         return (ps != null && ps.isSharedUser()) ? ps.sharedUser.name : null;
     }
 
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private String[] getPackagesForSharedUserIdLocked(String sharedUserId, int userId) {
         try {
             final SharedUserSetting sus = mSettings.getSharedUserLPw(
@@ -24045,7 +23639,7 @@
         mContext.enforceCallingOrSelfPermission(
                 Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
                 "setRuntimePermissionVersion");
-        synchronized (mPackages) {
+        synchronized (mLock) {
             return mSettings.getDefaultRuntimePermissionsVersionLPr(userId);
         }
     }
@@ -24057,13 +23651,13 @@
         mContext.enforceCallingOrSelfPermission(
                 Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
                 "setRuntimePermissionVersion");
-        synchronized (mPackages) {
+        synchronized (mLock) {
             mSettings.setDefaultRuntimePermissionsVersionLPr(version, userId);
         }
     }
 
     void forEachPackage(Consumer<PackageParser.Package> actionLocked) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             int numPackages = mPackages.size();
             for (int i = 0; i < numPackages; i++) {
                 actionLocked.accept(mPackages.valueAt(i));
@@ -24073,7 +23667,7 @@
 
     void forEachInstalledPackage(@NonNull Consumer<PackageParser.Package> actionLocked,
             @UserIdInt int userId) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             int numPackages = mPackages.size();
             for (int i = 0; i < numPackages; i++) {
                 PackageParser.Package pkg = mPackages.valueAt(i);
@@ -24095,7 +23689,7 @@
      * @return A copy of the values of mPackages.
      */
     Collection<PackageParser.Package> getPackages() {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             return new ArrayList<>(mPackages.values());
         }
     }
@@ -24148,9 +23742,9 @@
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "get install reason");
-        synchronized (mPackages) {
+        synchronized (mLock) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (filterAppAccessLPr(ps, callingUid, userId)) {
+            if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
                 return PackageManager.INSTALL_REASON_UNKNOWN;
             }
             if (ps != null) {
@@ -24234,13 +23828,13 @@
         if (!isInstantApp(packageName, userId)) {
             return null;
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             return mInstantAppRegistry.getInstantAppAndroidIdLPw(packageName, userId);
         }
     }
 
     boolean canHaveOatDir(String packageName) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             PackageParser.Package p = mPackages.get(packageName);
             if (p == null) {
                 return false;
@@ -24265,7 +23859,7 @@
         final List<String> codePaths;
         final String oatDir;
         final PackageParser.Package pkg;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             pkg = mPackages.get(packageName);
         }
         instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
@@ -24286,7 +23880,7 @@
     Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) {
         Set<String> unusedPackages = new HashSet<>();
         long currentTimeInMillis = System.currentTimeMillis();
-        synchronized (mPackages) {
+        synchronized (mLock) {
             for (PackageParser.Package pkg : mPackages.values()) {
                 PackageSetting ps =  mSettings.mPackages.get(pkg.packageName);
                 if (ps == null) {
@@ -24321,7 +23915,7 @@
                     + SET_HARMFUL_APP_WARNINGS + " permission.");
         }
 
-        synchronized(mPackages) {
+        synchronized (mLock) {
             mSettings.setHarmfulAppWarningLPw(packageName, warning, userId);
             scheduleWritePackageRestrictionsLocked(userId);
         }
@@ -24342,7 +23936,7 @@
                     + SET_HARMFUL_APP_WARNINGS + " permission.");
         }
 
-        synchronized(mPackages) {
+        synchronized (mLock) {
             return mSettings.getHarmfulAppWarningLPr(packageName, userId);
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 58f262c..029673f 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -270,7 +270,8 @@
         updateAvailable = orig.updateAvailable;
     }
 
-    private PackageUserState modifyUserState(int userId) {
+    @VisibleForTesting
+    PackageUserState modifyUserState(int userId) {
         PackageUserState state = mUserState.get(userId);
         if (state == null) {
             state = new PackageUserState();
@@ -463,6 +464,18 @@
         state.harmfulAppWarning = harmfulAppWarning;
     }
 
+    void setUserState(int userId, PackageUserState otherState) {
+        setUserState(userId, otherState.ceDataInode, otherState.enabled, otherState.installed,
+                otherState.stopped, otherState.notLaunched, otherState.hidden,
+                otherState.distractionFlags, otherState.suspended, otherState.suspendingPackage,
+                otherState.dialogInfo, otherState.suspendedAppExtras,
+                otherState.suspendedLauncherExtras, otherState.instantApp,
+                otherState.virtualPreload, otherState.lastDisableAppCaller,
+                otherState.enabledComponents, otherState.disabledComponents,
+                otherState.domainVerificationStatus, otherState.appLinkGeneration,
+                otherState.installReason, otherState.harmfulAppWarning);
+    }
+
     ArraySet<String> getEnabledComponents(int userId) {
         return readUserState(userId).enabledComponents;
     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3bc2236..c1cb53d 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4020,7 +4020,7 @@
         String[] seinfos;
         int[] targetSdkVersions;
         int packagesCount;
-        synchronized (mPackages) {
+        synchronized (mLock) {
             Collection<PackageSetting> packages = mPackages.values();
             packagesCount = packages.size();
             volumeUuids = new String[packagesCount];
@@ -4064,7 +4064,7 @@
                 Slog.w(TAG, "Failed to prepare app data", e);
             }
         }
-        synchronized (mPackages) {
+        synchronized (mLock) {
             applyDefaultPreferredAppsLPw(userHandle);
         }
     }
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index bf141a0..bdeaf02 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -180,6 +180,8 @@
             checkDowngrade(session, activePackage, pkg);
             result.add(pkg);
         }
+        Slog.d(TAG, "Session " + session.sessionId + " has following APEX packages: ["
+                + result.stream().map(p -> p.packageName).collect(Collectors.joining(",")) + "]");
         return result;
     }
 
@@ -206,7 +208,7 @@
             throws PackageManagerException {
         final long activeVersion = activePackage.applicationInfo.longVersionCode;
         final long newVersionCode = newPackage.applicationInfo.longVersionCode;
-        boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
+        final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
                 session.params.installFlags, activePackage.applicationInfo.flags);
         if (activeVersion > newVersionCode && !allowsDowngrade) {
             if (!mApexManager.abortActiveSession()) {
@@ -225,6 +227,7 @@
     }
 
     private void preRebootVerification(@NonNull PackageInstallerSession session) {
+        Slog.d(TAG, "Starting preRebootVerification for session " + session.sessionId);
         final boolean hasApex = sessionContainsApex(session);
         // APEX checks. For single-package sessions, check if they contain an APEX. For
         // multi-package sessions, find all the child sessions that contain an APEX.
@@ -243,6 +246,8 @@
 
         if (sessionContainsApk(session)) {
             try {
+                Slog.d(TAG, "Running a pre-reboot verification for APKs in session "
+                        + session.sessionId + " by performing a dry-run install");
                 installApksInSession(session, /* preReboot */ true);
                 // TODO(b/118865310): abort the session on apexd.
             } catch (PackageManagerException e) {
@@ -277,6 +282,7 @@
         // On the other hand, if the order of the calls was inverted (first call apexd, then mark
         // session as ready), then if a device gets rebooted right after the call to apexd, only
         // apex part of the train will be applied, leaving device in an inconsistent state.
+        Slog.d(TAG, "Marking session " + session.sessionId + " as ready");
         session.setStagedSessionReady();
         if (!hasApex) {
             // Session doesn't contain apex, nothing to do.
@@ -315,6 +321,7 @@
     }
 
     private void resumeSession(@NonNull PackageInstallerSession session) {
+        Slog.d(TAG, "Resuming session " + session.sessionId);
         final boolean hasApex = sessionContainsApex(session);
         if (hasApex) {
             // Check with apexservice whether the apex packages have been activated.
@@ -348,9 +355,12 @@
                         + "retry at next reboot.");
                 return;
             }
+            Slog.i(TAG, "APEX packages in session " + session.sessionId
+                    + " were successfully activated. Proceeding with APK packages, if any");
         }
         // The APEX part of the session is activated, proceed with the installation of APKs.
         try {
+            Slog.d(TAG, "Installing APK packages in session " + session.sessionId);
             installApksInSession(session, /* preReboot */ false);
         } catch (PackageManagerException e) {
             session.setStagedSessionFailed(e.error, e.getMessage());
@@ -370,6 +380,7 @@
             return;
         }
 
+        Slog.d(TAG, "Marking session " + session.sessionId + " as applied");
         session.setStagedSessionApplied();
         if (hasApex) {
             mApexManager.markStagedSessionSuccessful(session.sessionId);
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index b829f0b..f7b60c2 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -8,6 +8,20 @@
     },
     {
       "name": "CtsCompilationTestCases"
+    },
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.pm."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
     }
   ],
   "postsubmit": [
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index fe0b3a3..3e655ed 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1170,6 +1170,11 @@
                 final int flags = mContext.getPackageManager().getPermissionFlags(
                         permission, pkg.packageName, user);
 
+                // If we are trying to grant as system fixed and already system fixed
+                // then the system can change the system fixed grant state.
+                final boolean changingGrantForSystemFixed = systemFixed
+                        && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0;
+
                 // Certain flags imply that the permission's current state by the system or
                 // device/profile owner or the user. In these cases we do not want to clobber the
                 // current state.
@@ -1177,7 +1182,8 @@
                 // Unless the caller wants to override user choices. The override is
                 // to make sure we can grant the needed permission to the default
                 // sms and phone apps after the user chooses this in the UI.
-                if (!isFixedOrUserSet(flags) || ignoreSystemPackage) {
+                if (!isFixedOrUserSet(flags) || ignoreSystemPackage
+                        || changingGrantForSystemFixed) {
                     // Never clobber policy fixed permissions.
                     // We must allow the grant of a system-fixed permission because
                     // system-fixed is sticky, but the permission itself may be revoked.
@@ -1196,6 +1202,14 @@
                                 PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, user);
                     }
 
+                    // If the system tries to change a system fixed permission from one fixed
+                    // state to another we need to drop the fixed flag to allow the grant.
+                    if (changingGrantForSystemFixed) {
+                        mContext.getPackageManager().updatePermissionFlags(permission,
+                                pkg.packageName, flags,
+                                flags & ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, user);
+                    }
+
                     if (pm.checkPermission(permission, pkg.packageName)
                             != PackageManager.PERMISSION_GRANTED) {
                         mContext.getPackageManager()
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 673ab6c..210a7af 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -54,6 +54,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.AppOpsManager;
 import android.app.ApplicationPackageManager;
 import android.app.IActivityManager;
 import android.content.Context;
@@ -124,6 +125,7 @@
 import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultHomeProvider;
 import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionsState.PermissionState;
+import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.policy.SoftRestrictedPermissionPolicy;
 
 import libcore.util.EmptyArray;
@@ -226,6 +228,9 @@
     @GuardedBy("mLock")
     private boolean mSystemReady;
 
+    @GuardedBy("mLock")
+    private PermissionPolicyInternal mPermissionPolicyInternal;
+
     /**
      * For each foreground/background permission the mapping:
      * Background permission -> foreground permissions
@@ -1447,10 +1452,10 @@
     /**
      * Reverts user permission state changes (permissions and flags).
      *
-     * @param ps The package for which to reset.
+     * @param pkg The package for which to reset.
      * @param userId The device user for which to do a reset.
      */
-    @GuardedBy("mPackages")
+    @GuardedBy("mLock")
     private void resetRuntimePermissionsInternal(final PackageParser.Package pkg,
             final int userId) {
         final String packageName = pkg.packageName;
@@ -1521,6 +1526,7 @@
             }
         };
 
+        final AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
         for (int i = 0; i < permissionCount; i++) {
             final String permName = pkg.requestedPermissions.get(i);
             final BasePermission bp;
@@ -1588,6 +1594,14 @@
             if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0) {
                 grantRuntimePermissionInternal(permName, packageName, false,
                         Process.SYSTEM_UID, userId, delayingPermCallback);
+                // Allow app op later as we are holding mPackages
+                // PermissionPolicyService will handle the app op for foreground/background
+                // permissions.
+                String appOp = AppOpsManager.permissionToOp(permName);
+                if (appOp != null) {
+                    mHandler.post(() -> appOpsManager.setUidMode(appOp, uid,
+                            AppOpsManager.MODE_ALLOWED));
+                }
             // If permission review is enabled the permissions for a legacy apps
             // are represented as constantly granted runtime ones, so don't revoke.
             } else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
@@ -2433,6 +2447,13 @@
                             boolean softRestricted = bp.isSoftRestricted();
 
                             for (int userId : currentUserIds) {
+                                // If permission policy is not ready we don't deal with restricted
+                                // permissions as the policy may whitelist some permissions. Once
+                                // the policy is initialized we would re-evaluate permissions.
+                                final boolean permissionPolicyInitialized =
+                                        mPermissionPolicyInternal != null
+                                                && mPermissionPolicyInternal.isInitialized(userId);
+
                                 PermissionState permState = origPermissions
                                         .getRuntimePermissionState(perm, userId);
                                 int flags = permState != null ? permState.getFlags() : 0;
@@ -2447,7 +2468,7 @@
 
                                 if (appSupportsRuntimePermissions) {
                                     // If hard restricted we don't allow holding it
-                                    if (hardRestricted) {
+                                    if (permissionPolicyInitialized && hardRestricted) {
                                         if (!restrictionExempt) {
                                             if (permState != null && permState.isGranted()
                                                     && permissionsState.revokeRuntimePermission(
@@ -2460,7 +2481,7 @@
                                             }
                                         }
                                     // If soft restricted we allow holding in a restricted form
-                                    } else if (softRestricted) {
+                                    } else if (permissionPolicyInitialized && softRestricted) {
                                         // Regardless if granted set the restriction flag as it
                                         // may affect app treatment based on this permission.
                                         if (!restrictionExempt && !restrictionApplied) {
@@ -2479,7 +2500,8 @@
                                         flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
                                         wasChanged = true;
                                     // Hard restricted permissions cannot be held.
-                                    } else if (!hardRestricted || restrictionExempt) {
+                                    } else if (!permissionPolicyInitialized
+                                            || (!hardRestricted || restrictionExempt)) {
                                         if (permState != null && permState.isGranted()) {
                                             if (permissionsState.grantRuntimePermission(bp, userId)
                                                     == PERMISSION_OPERATION_FAILURE) {
@@ -2508,33 +2530,28 @@
 
                                     // If legacy app always grant the permission but if restricted
                                     // and not exempt take a note a restriction should be applied.
-                                    if ((hardRestricted || softRestricted)
-                                            && !restrictionExempt && !restrictionApplied) {
+                                    if (permissionPolicyInitialized
+                                            && (hardRestricted || softRestricted)
+                                                    && !restrictionExempt && !restrictionApplied) {
                                         flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
                                         wasChanged = true;
                                     }
                                 }
 
                                 // If unrestricted or restriction exempt, don't apply restriction.
-                                if (!(hardRestricted || softRestricted) || restrictionExempt)  {
-                                    if (restrictionApplied) {
-                                        flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
-                                        // Dropping restriction on a legacy app requires a review.
-                                        if (!appSupportsRuntimePermissions) {
-                                            flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+                                if (permissionPolicyInitialized) {
+                                    if (!(hardRestricted || softRestricted) || restrictionExempt) {
+                                        if (restrictionApplied) {
+                                            flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
+                                            // Dropping restriction on a legacy app implies a review
+                                            if (!appSupportsRuntimePermissions) {
+                                                flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+                                            }
+                                            wasChanged = true;
                                         }
-                                        wasChanged = true;
                                     }
                                 }
 
-                                if (hardRestricted && !restrictionExempt
-                                        && (flags & FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
-                                    // Applying a hard restriction implies revoking it. This might
-                                    // lead to a system-fixed, revoked permission.
-                                    flags &= ~FLAG_PERMISSION_SYSTEM_FIXED;
-                                    wasChanged = true;
-                                }
-
                                 if (wasChanged) {
                                     updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
                                 }
@@ -2569,6 +2586,13 @@
                             boolean softRestricted = bp.isSoftRestricted();
 
                             for (int userId : currentUserIds) {
+                                // If permission policy is not ready we don't deal with restricted
+                                // permissions as the policy may whitelist some permissions. Once
+                                // the policy is initialized we would re-evaluate permissions.
+                                final boolean permissionPolicyInitialized =
+                                        mPermissionPolicyInternal != null
+                                                && mPermissionPolicyInternal.isInitialized(userId);
+
                                 boolean wasChanged = false;
 
                                 boolean restrictionExempt =
@@ -2579,7 +2603,7 @@
 
                                 if (appSupportsRuntimePermissions) {
                                     // If hard restricted we don't allow holding it
-                                    if (hardRestricted) {
+                                    if (permissionPolicyInitialized && hardRestricted) {
                                         if (!restrictionExempt) {
                                             if (permState != null && permState.isGranted()
                                                     && permissionsState.revokeRuntimePermission(
@@ -2592,7 +2616,7 @@
                                             }
                                         }
                                     // If soft restricted we allow holding in a restricted form
-                                    } else if (softRestricted) {
+                                    } else if (permissionPolicyInitialized && softRestricted) {
                                         // Regardless if granted set the  restriction flag as it
                                         // may affect app treatment based on this permission.
                                         if (!restrictionExempt && !restrictionApplied) {
@@ -2611,7 +2635,8 @@
                                         flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
                                         wasChanged = true;
                                     // Hard restricted permissions cannot be held.
-                                    } else if (!hardRestricted || restrictionExempt) {
+                                    } else if (!permissionPolicyInitialized ||
+                                            (!hardRestricted || restrictionExempt)) {
                                         if (permissionsState.grantRuntimePermission(bp, userId) !=
                                                 PERMISSION_OPERATION_FAILURE) {
                                              wasChanged = true;
@@ -2627,22 +2652,25 @@
 
                                     // If legacy app always grant the permission but if restricted
                                     // and not exempt take a note a restriction should be applied.
-                                    if ((hardRestricted || softRestricted)
-                                            && !restrictionExempt && !restrictionApplied) {
+                                    if (permissionPolicyInitialized
+                                            && (hardRestricted || softRestricted)
+                                                    && !restrictionExempt && !restrictionApplied) {
                                         flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
                                         wasChanged = true;
                                     }
                                 }
 
                                 // If unrestricted or restriction exempt, don't apply restriction.
-                                if (!(hardRestricted || softRestricted) || restrictionExempt)  {
-                                    if (restrictionApplied) {
-                                        flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
-                                        // Dropping restriction on a legacy app requires a review.
-                                        if (!appSupportsRuntimePermissions) {
-                                            flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+                                if (permissionPolicyInitialized) {
+                                    if (!(hardRestricted || softRestricted) || restrictionExempt) {
+                                        if (restrictionApplied) {
+                                            flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
+                                            // Dropping restriction on a legacy app implies a review
+                                            if (!appSupportsRuntimePermissions) {
+                                                flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+                                            }
+                                            wasChanged = true;
                                         }
-                                        wasChanged = true;
                                     }
                                 }
 
@@ -3949,6 +3977,7 @@
         }
 
         mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
+        mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class);
 
         int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;
         for (int userId : UserManagerService.getInstance().getUserIds()) {
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
index 7760c1e..6084c67 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.Intent;
 
 /**
@@ -26,6 +27,19 @@
 public abstract class PermissionPolicyInternal {
 
     /**
+     * Callback for initializing the permission policy service.
+     */
+    public interface OnInitializedCallback {
+
+        /**
+         * Called when initialized for the given user.
+         *
+         * @param userId The initialized user.
+         */
+        void onInitialized(@UserIdInt int userId);
+    }
+
+    /**
      * Check whether an activity should be started.
      *
      * @param intent the {@link Intent} for the activity start
@@ -36,4 +50,17 @@
      */
     public abstract boolean checkStartActivity(@NonNull Intent intent, int callingUid,
             @Nullable String callingPackage);
+
+    /**
+     * @return Whether the policy is initialized for a user.
+     */
+    public abstract boolean isInitialized(@UserIdInt int userId);
+
+    /**
+     * Set a callback for users being initialized. If the user is already
+     * initialized the callback will not be invoked.
+     *
+     * @param callback The callback to register.
+     */
+    public abstract void setOnInitializedCallback(@NonNull OnInitializedCallback callback);
 }
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 8da7f7b..f68a06b 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -66,6 +66,7 @@
 import com.android.server.SystemService;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
+import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
 import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
 
@@ -86,6 +87,10 @@
     @GuardedBy("mLock")
     private final SparseBooleanArray mIsStarted = new SparseBooleanArray();
 
+    /** Callbacks for when a user is initialized */
+    @GuardedBy("mLock")
+    private OnInitializedCallback mOnInitializedCallback;
+
     /**
      * Whether an async {@link #synchronizePackagePermissionsAndAppOpsForUser} is currently
      * scheduled for a package/user.
@@ -240,12 +245,20 @@
 
         grantOrUpgradeDefaultRuntimePermissionsIfNeeded(userId);
 
+        final OnInitializedCallback callback;
+
         synchronized (mLock) {
             mIsStarted.put(userId, true);
+            callback = mOnInitializedCallback;
         }
 
         // Force synchronization as permissions might have changed
         synchronizePermissionsAndAppOpsForUser(userId);
+
+        // Tell observers we are initialized for this user.
+        if (callback != null) {
+            callback.onInitialized(userId);
+        }
     }
 
     @Override
@@ -809,6 +822,18 @@
             return true;
         }
 
+        @Override
+        public boolean isInitialized(int userId) {
+            return isStarted(userId);
+        }
+
+        @Override
+        public void setOnInitializedCallback(@NonNull OnInitializedCallback callback) {
+            synchronized (mLock) {
+                mOnInitializedCallback = callback;
+            }
+        }
+
         /**
          * Check if the intent action is removed for the calling package (often based on target SDK
          * version). If the action is removed, we'll silently cancel the activity launch.
diff --git a/services/core/java/com/android/server/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java
index e139ab8..4970862 100644
--- a/services/core/java/com/android/server/slice/PinnedSliceState.java
+++ b/services/core/java/com/android/server/slice/PinnedSliceState.java
@@ -188,7 +188,7 @@
             b.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
             try {
                 client.call(SliceProvider.METHOD_PIN, null, b);
-            } catch (RemoteException e) {
+            } catch (Exception e) {
                 Log.w(TAG, "Unable to contact " + mUri, e);
             }
         }
@@ -201,7 +201,7 @@
             b.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
             try {
                 client.call(SliceProvider.METHOD_UNPIN, null, b);
-            } catch (RemoteException e) {
+            } catch (Exception e) {
                 Log.w(TAG, "Unable to contact " + mUri, e);
             }
         }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 015464e..31e8bbdab 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -30,6 +30,7 @@
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_UNDEFINED;
 import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -2096,6 +2097,7 @@
                         pendingOptions.getRemoteAnimationAdapter());
                 break;
             case ANIM_NONE:
+            case ANIM_UNDEFINED:
                 break;
             default:
                 Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index beff5fb..1c56a10 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1191,8 +1191,7 @@
         final PackageInfo packageInfo;
         try {
             packageInfo = mService.mContext.getPackageManager()
-                    .getPackageInfoAsUser(callingPackage, PackageManager.GET_PERMISSIONS,
-                            UserHandle.getUserId(callingUid));
+                    .getPackageInfo(callingPackage, PackageManager.GET_PERMISSIONS);
         } catch (PackageManager.NameNotFoundException e) {
             Slog.i(TAG, "Cannot find package info for " + callingPackage);
             return ACTIVITY_RESTRICTION_NONE;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index a334ed8..9ed93c4 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -187,6 +187,7 @@
     private boolean mNoAnimation;
     private boolean mKeepCurTransition;
     private boolean mAvoidMoveToFront;
+    private boolean mFrozeTaskList;
 
     // We must track when we deliver the new intent since multiple code paths invoke
     // {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used
@@ -483,6 +484,7 @@
         mNoAnimation = starter.mNoAnimation;
         mKeepCurTransition = starter.mKeepCurTransition;
         mAvoidMoveToFront = starter.mAvoidMoveToFront;
+        mFrozeTaskList = starter.mFrozeTaskList;
 
         mVoiceSession = starter.mVoiceSession;
         mVoiceInteractor = starter.mVoiceInteractor;
@@ -1093,6 +1095,14 @@
 
     void postStartActivityProcessing(ActivityRecord r, int result,
             ActivityStack startedActivityStack) {
+        if (!ActivityManager.isStartResultSuccessful(result)) {
+            if (mFrozeTaskList) {
+                // If we specifically froze the task list as part of starting an activity, then
+                // reset the frozen list state if it failed to start. This is normally otherwise
+                // called when the freeze-timeout has elapsed.
+                mSupervisor.mRecentTasks.resetFreezeTaskListReorderingOnTimeout();
+            }
+        }
         if (ActivityManager.isStartResultFatalError(result)) {
             return;
         }
@@ -1485,6 +1495,14 @@
                 mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
                         : DEFAULT_DISPLAY;
 
+        // If requested, freeze the task list
+        if (mOptions != null && mOptions.freezeRecentTasksReordering()
+                && mSupervisor.mRecentTasks.isCallerRecents(r.launchedFromUid)
+                && !mSupervisor.mRecentTasks.isFreezeTaskListReorderingSet()) {
+            mFrozeTaskList = true;
+            mSupervisor.mRecentTasks.setFreezeTaskListReordering();
+        }
+
         // Do not start home activity if it cannot be launched on preferred display. We are not
         // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
         // fallback to launch on other displays.
@@ -1776,6 +1794,7 @@
         mNoAnimation = false;
         mKeepCurTransition = false;
         mAvoidMoveToFront = false;
+        mFrozeTaskList = false;
 
         mVoiceSession = null;
         mVoiceInteractor = null;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7e741a0..b351faf 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1012,7 +1012,6 @@
     public final int startActivities(IApplicationThread caller, String callingPackage,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
             int userId) {
-        assertPackageMatchesCallingUid(callingPackage);
         final String reason = "startActivities";
         enforceNotIsolatedCaller(reason);
         userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
@@ -1032,11 +1031,10 @@
                 true /*validateIncomingUser*/);
     }
 
-    private int startActivityAsUser(IApplicationThread caller, String callingPackage,
+    int startActivityAsUser(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
             boolean validateIncomingUser) {
-        assertPackageMatchesCallingUid(callingPackage);
         enforceNotIsolatedCaller("startActivityAsUser");
 
         userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
@@ -1209,7 +1207,6 @@
     public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
-        assertPackageMatchesCallingUid(callingPackage);
         final WaitResult res = new WaitResult();
         synchronized (mGlobalLock) {
             enforceNotIsolatedCaller("startActivityAndWait");
@@ -1237,7 +1234,6 @@
     public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, Configuration config, Bundle bOptions, int userId) {
-        assertPackageMatchesCallingUid(callingPackage);
         synchronized (mGlobalLock) {
             enforceNotIsolatedCaller("startActivityWithConfig");
             userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
@@ -1287,7 +1283,6 @@
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, IBinder permissionToken,
             boolean ignoreTargetSecurity, int userId) {
-        assertPackageMatchesCallingUid(callingPackage);
         // This is very dangerous -- it allows you to perform a start activity (including
         // permission grants) as any app that may launch one of your own activities.  So we only
         // allow this in two cases:
@@ -1417,7 +1412,6 @@
             Intent intent, String resolvedType, IVoiceInteractionSession session,
             IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
             Bundle bOptions, int userId) {
-        assertPackageMatchesCallingUid(callingPackage);
         mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
         if (session == null || interactor == null) {
             throw new NullPointerException("null session or interactor");
@@ -1441,7 +1435,6 @@
     @Override
     public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
             Intent intent, String resolvedType, Bundle bOptions, int userId) {
-        assertPackageMatchesCallingUid(callingPackage);
         mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
         userId = handleIncomingUser(callingPid, callingUid, userId, "startAssistantActivity");
 
@@ -2367,9 +2360,15 @@
     void moveTaskToFrontLocked(@Nullable IApplicationThread appThread,
             @Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options,
             boolean fromRecents) {
+
         final int callingPid = Binder.getCallingPid();
         final int callingUid = Binder.getCallingUid();
-        assertPackageMatchesCallingUid(callingPackage);
+        if (!isSameApp(callingUid, callingPackage)) {
+            String msg = "Permission Denial: moveTaskToFrontLocked() from pid="
+                    + Binder.getCallingPid() + " as package " + callingPackage;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
         if (!checkAppSwitchAllowedLocked(callingPid, callingUid, -1, -1, "Task to front")) {
             SafeActivityOptions.abort(options);
             return;
@@ -2421,7 +2420,7 @@
     /**
      * Return true if callingUid is system, or packageName belongs to that callingUid.
      */
-    private boolean isSameApp(int callingUid, @Nullable String packageName) {
+    boolean isSameApp(int callingUid, @Nullable String packageName) {
         try {
             if (callingUid != 0 && callingUid != SYSTEM_UID) {
                 if (packageName == null) {
@@ -2438,21 +2437,6 @@
         return true;
     }
 
-    /**
-     * Checks that the provided package name matches the current calling UID, throws a security
-     * exception if it doesn't.
-     */
-    void assertPackageMatchesCallingUid(@Nullable String packageName) {
-        final int callingUid = Binder.getCallingUid();
-        if (isSameApp(callingUid, packageName)) {
-            return;
-        }
-        final String msg = "Permission Denial: package=" + packageName
-                + " does not belong to uid=" + callingUid;
-        Slog.w(TAG, msg);
-        throw new SecurityException(msg);
-    }
-
     boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
             int callingPid, int callingUid, String name) {
         if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
@@ -2986,7 +2970,6 @@
     @Override
     public List<IBinder> getAppTasks(String callingPackage) {
         int callingUid = Binder.getCallingUid();
-        assertPackageMatchesCallingUid(callingPackage);
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -6093,7 +6076,6 @@
                 SafeActivityOptions options, int userId, boolean validateIncomingUser,
                 PendingIntentRecord originatingPendingIntent,
                 boolean allowBackgroundActivityStart) {
-            assertPackageMatchesCallingUid(callingPackage);
             synchronized (mGlobalLock) {
                 return getActivityStartController().startActivitiesInPackage(uid, realCallingPid,
                         realCallingUid, callingPackage, intents, resolvedTypes, resultTo, options,
@@ -6109,7 +6091,6 @@
                 int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
                 PendingIntentRecord originatingPendingIntent,
                 boolean allowBackgroundActivityStart) {
-            assertPackageMatchesCallingUid(callingPackage);
             synchronized (mGlobalLock) {
                 return getActivityStartController().startActivityInPackage(uid, realCallingPid,
                         realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho,
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index a8f7768..1eb7455 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -27,6 +27,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.UserHandle;
+import android.util.Slog;
 
 /**
  * An implementation of IAppTask, that allows an app to manage its own tasks via
@@ -96,7 +97,12 @@
         // Will bring task to front if it already has a root activity.
         final int callingPid = Binder.getCallingPid();
         final int callingUid = Binder.getCallingUid();
-        mService.assertPackageMatchesCallingUid(callingPackage);
+        if (!mService.isSameApp(callingUid, callingPackage)) {
+            String msg = "Permission Denial: moveToFront() from pid="
+                    + Binder.getCallingPid() + " as package " + callingPackage;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mService.mGlobalLock) {
@@ -128,7 +134,6 @@
     public int startActivity(IBinder whoThread, String callingPackage,
             Intent intent, String resolvedType, Bundle bOptions) {
         checkCaller();
-        mService.assertPackageMatchesCallingUid(callingPackage);
 
         int callingUser = UserHandle.getCallingUserId();
         TaskRecord tr;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index d15081c..aefc152 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1015,6 +1015,8 @@
     void notifyAppStopped() {
         if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: " + this);
         mAppStopped = true;
+        // Reset the last saved PiP snap fraction on app stop.
+        mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this);
         destroySurfaces();
         // Remove any starting window that was added for this app if they are still around.
         removeStartingWindow();
@@ -2485,7 +2487,7 @@
         // transformed the task.
         final RecentsAnimationController controller = mWmService.getRecentsAnimationController();
         if (controller != null && controller.isAnimatingTask(getTask())
-                && controller.shouldCancelWithDeferredScreenshot()) {
+                && controller.shouldDeferCancelUntilNextTransition()) {
             return false;
         }
 
@@ -3076,11 +3078,6 @@
     @Override
     void setHidden(boolean hidden) {
         super.setHidden(hidden);
-
-        if (hidden) {
-            // Once the app window is hidden, reset the last saved PiP snap fraction
-            mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this);
-        }
         scheduleAnimation();
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index a46fa13..207e8ef 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -30,6 +30,7 @@
 import android.annotation.Nullable;
 import android.app.WindowConfiguration;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.provider.Settings;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -64,6 +65,11 @@
 class DisplayWindowSettings {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayWindowSettings" : TAG_WM;
 
+    private static final String SYSTEM_DIRECTORY = "system";
+    private static final String DISPLAY_SETTINGS_FILE_NAME = "display_settings.xml";
+    private static final String VENDOR_DISPLAY_SETTINGS_PATH = "etc/" + DISPLAY_SETTINGS_FILE_NAME;
+    private static final String WM_DISPLAY_COMMIT_TAG = "wm-displays";
+
     private static final int IDENTIFIER_UNIQUE_ID = 0;
     private static final int IDENTIFIER_PORT = 1;
     @IntDef(prefix = { "IDENTIFIER_" }, value = {
@@ -688,8 +694,26 @@
         private final AtomicFile mAtomicFile;
 
         AtomicFileStorage() {
-            final File folder = new File(Environment.getDataDirectory(), "system");
-            mAtomicFile = new AtomicFile(new File(folder, "display_settings.xml"), "wm-displays");
+            final File folder = new File(Environment.getDataDirectory(), SYSTEM_DIRECTORY);
+            final File settingsFile = new File(folder, DISPLAY_SETTINGS_FILE_NAME);
+            // If display_settings.xml doesn't exist, try to copy the vendor's one instead
+            // in order to provide the vendor specific initialization.
+            if (!settingsFile.exists()) {
+                copyVendorSettings(settingsFile);
+            }
+            mAtomicFile = new AtomicFile(settingsFile, WM_DISPLAY_COMMIT_TAG);
+        }
+
+        private static void copyVendorSettings(File target) {
+            final File vendorFile = new File(Environment.getVendorDirectory(),
+                    VENDOR_DISPLAY_SETTINGS_PATH);
+            if (vendorFile.canRead()) {
+                try {
+                    FileUtils.copy(vendorFile, target);
+                } catch (IOException e) {
+                    Slog.e(TAG, "Failed to copy vendor display_settings.xml");
+                }
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 422b6e5..85ba95f 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -90,24 +90,11 @@
     }
 
     /**
-     * @return true if either Keyguard or AOD are showing, not going away, and not being occluded
-     *         on the given display, false otherwise.
+     * @return true if either 1) AOD is showing, or 2) Keyguard is showing, not going away, and not
+     *         being occluded on the given display, false otherwise.
      */
     boolean isKeyguardOrAodShowing(int displayId) {
-        return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway
-                && !isDisplayOccluded(displayId);
-    }
-
-    /**
-     * @return {@code true} for default display when AOD is showing. Otherwise, same as
-     *         {@link #isKeyguardOrAodShowing(int)}
-     * TODO(b/125198167): Replace isKeyguardOrAodShowing() by this logic.
-     */
-    boolean isKeyguardUnoccludedOrAodShowing(int displayId) {
-        if (displayId == DEFAULT_DISPLAY && mAodShowing) {
-            return true;
-        }
-        return isKeyguardOrAodShowing(displayId);
+        return mAodShowing || isKeyguardShowing(displayId);
     }
 
     /**
@@ -115,7 +102,7 @@
      *         display, false otherwise
      */
     boolean isKeyguardShowing(int displayId) {
-        return mKeyguardShowing && !mKeyguardGoingAway && !isDisplayOccluded(displayId);
+        return mKeyguardShowing && !mKeyguardGoingAway && !isKeyguardOccluded(displayId);
     }
 
     /**
@@ -328,7 +315,7 @@
             return;
         }
 
-        mWindowManager.onKeyguardOccludedChanged(isDisplayOccluded(DEFAULT_DISPLAY));
+        mWindowManager.onKeyguardOccludedChanged(isKeyguardOccluded(DEFAULT_DISPLAY));
         if (isKeyguardLocked()) {
             mWindowManager.deferSurfaceLayout();
             try {
@@ -373,7 +360,7 @@
         }
     }
 
-    private boolean isDisplayOccluded(int displayId) {
+    private boolean isKeyguardOccluded(int displayId) {
         return getDisplay(displayId).mOccluded;
     }
 
@@ -391,13 +378,13 @@
         if (mBeforeUnoccludeTransit != TRANSIT_UNSET
                 && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
                 // TODO(b/113840485): Handle app transition for individual display.
-                && isDisplayOccluded(DEFAULT_DISPLAY)) {
+                && isKeyguardOccluded(DEFAULT_DISPLAY)) {
 
             // Reuse old transit in case we are occluding Keyguard again, meaning that we never
             // actually occclude/unocclude Keyguard, but just run a normal transition.
             return mBeforeUnoccludeTransit;
             // TODO(b/113840485): Handle app transition for individual display.
-        } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) {
+        } else if (!isKeyguardOccluded(DEFAULT_DISPLAY)) {
 
             // Save transit in case we dismiss/occlude Keyguard shortly after.
             mBeforeUnoccludeTransit = dc.mAppTransition.getAppTransition();
@@ -409,7 +396,7 @@
 
     private void dismissDockedStackIfNeeded() {
         // TODO(b/113840485): Handle docked stack for individual display.
-        if (mKeyguardShowing && isDisplayOccluded(DEFAULT_DISPLAY)) {
+        if (mKeyguardShowing && isKeyguardOccluded(DEFAULT_DISPLAY)) {
             // The lock screen is currently showing, but is occluded by a window that can
             // show on top of the lock screen. In this can we want to dismiss the docked
             // stack since it will be complicated/risky to try to put the activity on top
@@ -434,9 +421,9 @@
 
     private void updateKeyguardSleepToken(int displayId) {
         final KeyguardDisplayState state = getDisplay(displayId);
-        if (isKeyguardUnoccludedOrAodShowing(displayId) && state.mSleepToken == null) {
+        if (isKeyguardOrAodShowing(displayId) && state.mSleepToken == null) {
             state.acquiredSleepToken();
-        } else if (!isKeyguardUnoccludedOrAodShowing(displayId) && state.mSleepToken != null) {
+        } else if (!isKeyguardOrAodShowing(displayId) && state.mSleepToken != null) {
             state.releaseSleepToken();
         }
     }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index bf627ec..0a3e7a4 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -355,7 +355,7 @@
                         // launch-behind state is restored. That also prevents the next transition
                         // type being disturbed if the visibility is updated after setting the next
                         // transition (the target activity will be one of closing apps).
-                        if (!controller.shouldCancelWithDeferredScreenshot()
+                        if (!controller.shouldDeferCancelWithScreenshot()
                                 && !targetStack.isFocusedStackOnDisplay()) {
                             targetStack.ensureActivitiesVisibleLocked(null /* starting */,
                                     0 /* starting */, false /* preserveWindows */);
@@ -415,16 +415,18 @@
         final DisplayContent dc =
                 mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent;
         dc.mBoundsAnimationController.setAnimationType(
-                controller.shouldCancelWithDeferredScreenshot() ? FADE_IN : BOUNDS);
+                controller.shouldDeferCancelUntilNextTransition() ? FADE_IN : BOUNDS);
 
-        // Cancel running recents animation and screenshot previous task when the next
-        // transition starts in below cases:
-        // 1) The next launching task is not in recents animation task.
+        // We defer canceling the recents animation until the next app transition in the following
+        // cases:
+        // 1) The next launching task is not being animated by the recents animation
         // 2) The next task is home activity. (i.e. pressing home key to back home in recents).
         if ((!controller.isAnimatingTask(stack.getTaskStack().getTopChild())
                 || controller.isTargetApp(stack.getTopActivity().mAppWindowToken))
-                && controller.shouldCancelWithDeferredScreenshot()) {
-            controller.cancelOnNextTransitionStart();
+                && controller.shouldDeferCancelUntilNextTransition()) {
+            // Always prepare an app transition since we rely on the transition callbacks to cleanup
+            mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+            controller.setCancelOnNextTransitionStart();
         } else {
             // Just cancel directly to unleash from launcher when the next launching task is the
             // current top task.
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 163be1e..6ea4d58 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -97,10 +97,9 @@
     private final Runnable mFailsafeRunnable = () ->
             cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable");
 
-    final Object mLock = new Object();
-
     // The recents component app token that is shown behind the visibile tasks
     private AppWindowToken mTargetAppToken;
+    private DisplayContent mDisplayContent;
     private int mTargetActivityType;
     private Rect mMinimizedHomeBounds = new Rect();
 
@@ -124,25 +123,47 @@
 
     private boolean mLinkedToDeathOfRunner;
 
-    private boolean mCancelWithDeferredScreenshot;
-
+    // Whether to try to defer canceling from a stack order change until the next transition
+    private boolean mRequestDeferCancelUntilNextTransition;
+    // Whether to actually defer canceling until the next transition
     private boolean mCancelOnNextTransitionStart;
+    // Whether to take a screenshot when handling a deferred cancel
+    private boolean mCancelDeferredWithScreenshot;
 
     /**
      * Animates the screenshot of task that used to be controlled by RecentsAnimation.
-     * @see {@link #cancelOnNextTransitionStart}
+     * @see {@link #setCancelOnNextTransitionStart}
      */
     SurfaceAnimator mRecentScreenshotAnimator;
 
+    /**
+     * An app transition listener to cancel the recents animation only after the app transition
+     * starts or is canceled.
+     */
     final AppTransitionListener mAppTransitionListener = new AppTransitionListener() {
         @Override
         public int onAppTransitionStartingLocked(int transit, long duration,
                 long statusBarAnimationStartTime, long statusBarAnimationDuration) {
-            onTransitionStart();
-            mService.mRoot.getDisplayContent(mDisplayId).mAppTransition
-                    .unregisterListener(this);
+            continueDeferredCancel();
             return 0;
         }
+
+        @Override
+        public void onAppTransitionCancelledLocked(int transit) {
+            continueDeferredCancel();
+        }
+
+        private void continueDeferredCancel() {
+            mDisplayContent.mAppTransition.unregisterListener(this);
+            if (mCanceled) {
+                return;
+            }
+
+            if (mCancelOnNextTransitionStart) {
+                mCancelOnNextTransitionStart = false;
+                cancelAnimationWithScreenshot(mCancelDeferredWithScreenshot);
+            }
+        }
     };
 
     public interface RecentsAnimationCallbacks {
@@ -202,8 +223,7 @@
                         ? REORDER_MOVE_TO_TOP
                         : REORDER_MOVE_TO_ORIGINAL_POSITION,
                         true /* runSynchronously */, sendUserLeaveHint);
-                final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
-                dc.mBoundsAnimationController.setAnimationType(FADE_IN);
+                mDisplayContent.mBoundsAnimationController.setAnimationType(FADE_IN);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -240,8 +260,7 @@
                     }
 
                     mInputConsumerEnabled = enabled;
-                    final InputMonitor inputMonitor =
-                            mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
+                    final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
                     inputMonitor.updateInputWindowsLw(true /*force*/);
                     mService.scheduleAnimationLocked();
                 }
@@ -282,15 +301,23 @@
         }
 
         @Override
+        @Deprecated
         public void setCancelWithDeferredScreenshot(boolean screenshot) {
-            synchronized (mLock) {
-                setCancelWithDeferredScreenshotLocked(screenshot);
+            synchronized (mService.mGlobalLock) {
+                setDeferredCancel(true /* deferred */, screenshot);
+            }
+        }
+
+        @Override
+        public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
+            synchronized (mService.mGlobalLock) {
+                setDeferredCancel(defer, screenshot);
             }
         }
 
         @Override
         public void cleanupScreenshot() {
-            synchronized (mLock) {
+            synchronized (mService.mGlobalLock) {
                 if (mRecentScreenshotAnimator != null) {
                     mRecentScreenshotAnimator.cancelAnimation();
                     mRecentScreenshotAnimator = null;
@@ -312,10 +339,7 @@
         mCallbacks = callbacks;
         mDisplayId = displayId;
         mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
-    }
-
-    public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) {
-        initialize(mService.mRoot.getDisplayContent(mDisplayId), targetActivityType, recentTaskIds);
+        mDisplayContent = service.mRoot.getDisplayContent(displayId);
     }
 
     /**
@@ -323,15 +347,15 @@
      * because it may call cancelAnimation() which needs to properly clean up the controller
      * in the window manager.
      */
-    @VisibleForTesting
-    void initialize(DisplayContent dc, int targetActivityType, SparseBooleanArray recentTaskIds) {
+    public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) {
         mTargetActivityType = targetActivityType;
-        dc.mAppTransition.registerListenerLocked(mAppTransitionListener);
+        mDisplayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
 
         // Make leashes for each of the visible/target tasks and add it to the recents animation to
         // be started
-        final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
-        final TaskStack targetStack = dc.getStack(WINDOWING_MODE_UNDEFINED, targetActivityType);
+        final ArrayList<Task> visibleTasks = mDisplayContent.getVisibleTasks();
+        final TaskStack targetStack = mDisplayContent.getStack(WINDOWING_MODE_UNDEFINED,
+                targetActivityType);
         if (targetStack != null) {
             for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
                 final Task t = targetStack.getChildAt(i);
@@ -365,29 +389,31 @@
         }
 
         // Adjust the wallpaper visibility for the showing target activity
-        final AppWindowToken recentsComponentAppToken = dc.getStack(WINDOWING_MODE_UNDEFINED,
-                targetActivityType).getTopChild().getTopFullscreenAppToken();
+        final AppWindowToken recentsComponentAppToken =
+                targetStack.getTopChild().getTopFullscreenAppToken();
         if (recentsComponentAppToken != null) {
             if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setHomeApp("
                     + recentsComponentAppToken.getName() + ")");
             mTargetAppToken = recentsComponentAppToken;
             if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) {
-                dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
-                dc.setLayoutNeeded();
+                mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+                mDisplayContent.setLayoutNeeded();
             }
         }
 
         // Save the minimized home height
-        final TaskStack dockedStack = dc.getSplitScreenPrimaryStackIgnoringVisibility();
-        dc.getDockedDividerController().getHomeStackBoundsInDockedMode(
-                dc.getConfiguration(),
+        final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+        mDisplayContent.getDockedDividerController().getHomeStackBoundsInDockedMode(
+                mDisplayContent.getConfiguration(),
                 dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(),
                 mMinimizedHomeBounds);
 
         mService.mWindowPlacerLocked.performSurfacePlacement();
 
         // Notify that the animation has started
-        mStatusBar.onRecentsAnimationStateChanged(true /* running */);
+        if (mStatusBar != null) {
+            mStatusBar.onRecentsAnimationStateChanged(true /* running */);
+        }
     }
 
     @VisibleForTesting
@@ -441,8 +467,7 @@
 
             // Perform layout if it was scheduled before to make sure that we get correct content
             // insets for the target app window after a rotation
-            final DisplayContent displayContent = mService.mRoot.getDisplayContent(mDisplayId);
-            displayContent.performLayout(false /* initial */, false /* updateInputWindows */);
+            mDisplayContent.performLayout(false /* initial */, false /* updateInputWindows */);
 
             final Rect minimizedHomeBounds = mTargetAppToken != null
                     && mTargetAppToken.inSplitScreenSecondaryWindowingMode()
@@ -480,9 +505,8 @@
         cancelAnimation(reorderMode, true /* runSynchronously */, false /* screenshot */, reason);
     }
 
-    void cancelAnimationWithScreenShot() {
-        cancelAnimation(REORDER_KEEP_IN_PLACE, true /* sync */, true /* screenshot */,
-                "stackOrderChanged");
+    void cancelAnimationWithScreenshot(boolean screenshot) {
+        cancelAnimation(REORDER_KEEP_IN_PLACE, true /* sync */, screenshot, "stackOrderChanged");
     }
 
     private void cancelAnimation(@ReorderMode int reorderMode, boolean runSynchronously,
@@ -496,21 +520,29 @@
             }
             mService.mH.removeCallbacks(mFailsafeRunnable);
             mCanceled = true;
-            try {
-                if (screenshot) {
-                    // Screen shot previous task when next task starts transition.
-                    final Task task = mPendingAnimations.get(0).mTask;
-                    screenshotRecentTask(task, reorderMode, runSynchronously);
+
+            if (screenshot) {
+                // Screen shot previous task when next task starts transition and notify the runner.
+                // We will actually finish the animation once the runner calls cleanUpScreenshot().
+                final Task task = mPendingAnimations.get(0).mTask;
+                screenshotRecentTask(task, reorderMode, runSynchronously);
+                try {
                     mRunner.onAnimationCanceled(true /* deferredWithScreenshot */);
-                    return;
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to cancel recents animation", e);
                 }
-                mRunner.onAnimationCanceled(false /* deferredWithScreenshot */);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to cancel recents animation", e);
+            } else {
+                // Otherwise, notify the runner and clean up the animation immediately
+                // Note: In the fallback case, this can trigger multiple onAnimationCancel() calls
+                // to the runner if we this actually triggers cancel twice on the caller
+                try {
+                    mRunner.onAnimationCanceled(false /* deferredWithScreenshot */);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to cancel recents animation", e);
+                }
+                mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
+                        false /* sendUserLeaveHint */);
             }
-            // Clean up and return to the previous app
-            mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
-                    false /* sendUserLeaveHint */);
         }
     }
 
@@ -523,27 +555,36 @@
      * screenshot, so that Launcher can still control the leash lifecycle & make the next app
      * transition animate smoothly without flickering.
      */
-    void cancelOnNextTransitionStart() {
+    void setCancelOnNextTransitionStart() {
         mCancelOnNextTransitionStart = true;
     }
 
-    void setCancelWithDeferredScreenshotLocked(boolean screenshot) {
-        mCancelWithDeferredScreenshot = screenshot;
+    /**
+     * Requests that we attempt to defer the cancel until the next app transition if we are
+     * canceling from a stack order change.  If {@param screenshot} is specified, then the system
+     * will replace the contents of the leash with a screenshot, which must be cleaned up when the
+     * runner calls cleanUpScreenshot().
+     */
+    void setDeferredCancel(boolean defer, boolean screenshot) {
+        mRequestDeferCancelUntilNextTransition = defer;
+        mCancelDeferredWithScreenshot = screenshot;
     }
 
-    boolean shouldCancelWithDeferredScreenshot() {
-        return mCancelWithDeferredScreenshot;
+    /**
+     * @return Whether we should defer the cancel from a stack order change until the next app
+     * transition.
+     */
+    boolean shouldDeferCancelUntilNextTransition() {
+        return mRequestDeferCancelUntilNextTransition;
     }
 
-    void onTransitionStart() {
-        if (mCanceled) {
-            return;
-        }
-
-        if (mCancelOnNextTransitionStart) {
-            mCancelOnNextTransitionStart = false;
-            cancelAnimationWithScreenShot();
-        }
+    /**
+     * @return Whether we should both defer the cancel from a stack order change until the next
+     * app transition, and also that the deferred cancel should replace the contents of the leash
+     * with a screenshot.
+     */
+    boolean shouldDeferCancelWithScreenshot() {
+        return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot;
     }
 
     void screenshotRecentTask(Task task, @ReorderMode int reorderMode, boolean runSynchronously) {
@@ -576,6 +617,7 @@
 
         // Clear any pending failsafe runnables
         mService.mH.removeCallbacks(mFailsafeRunnable);
+        mDisplayContent.mAppTransition.unregisterListener(mAppTransitionListener);
 
         // Clear references to the runner
         unlinkToDeathOfRunner();
@@ -589,21 +631,22 @@
         }
 
         // Update the input windows after the animation is complete
-        final InputMonitor inputMonitor =
-                mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
+        final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
         inputMonitor.updateInputWindowsLw(true /*force*/);
 
         // We have deferred all notifications to the target app as a part of the recents animation,
         // so if we are actually transitioning there, notify again here
         if (mTargetAppToken != null) {
             if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
-                mService.mRoot.getDisplayContent(mDisplayId)
-                        .mAppTransition.notifyAppTransitionFinishedLocked(mTargetAppToken.token);
+                mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(
+                        mTargetAppToken.token);
             }
         }
 
         // Notify that the animation has ended
-        mStatusBar.onRecentsAnimationStateChanged(false /* running */);
+        if (mStatusBar != null) {
+            mStatusBar.onRecentsAnimationStateChanged(false /* running */);
+        }
     }
 
     void scheduleFailsafe() {
@@ -630,8 +673,7 @@
 
         synchronized (mService.getWindowManagerLock()) {
             // Clear associated input consumers on runner death
-            final InputMonitor inputMonitor =
-                    mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
+            final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
             inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
         }
     }
@@ -827,5 +869,11 @@
         pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized);
         pw.print(innerPrefix); pw.println("mTargetAppToken=" + mTargetAppToken);
         pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper());
+        pw.print(innerPrefix); pw.println("mRequestDeferCancelUntilNextTransition="
+                + mRequestDeferCancelUntilNextTransition);
+        pw.print(innerPrefix); pw.println("mCancelOnNextTransitionStart="
+                + mCancelOnNextTransitionStart);
+        pw.print(innerPrefix); pw.println("mCancelDeferredWithScreenshot="
+                + mCancelDeferredWithScreenshot);
     }
 }
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index dc62877..b94a7dc 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -937,6 +937,7 @@
             }
         }
 
+        t.setEarlyWakeup();
         setSnapshotTransform(t, mSnapshotFinalMatrix, mExitTransformation.getAlpha());
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 0d18b30..f51d4c4 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -294,6 +294,10 @@
 
     private int getPreferredLaunchDisplay(@Nullable TaskRecord task,
             @Nullable ActivityOptions options, ActivityRecord source, LaunchParams currentParams) {
+        if (!mSupervisor.mService.mSupportsMultiDisplay) {
+            return DEFAULT_DISPLAY;
+        }
+
         int displayId = INVALID_DISPLAY;
         final int optionLaunchId = options != null ? options.getLaunchDisplayId() : INVALID_DISPLAY;
         if (optionLaunchId != INVALID_DISPLAY) {
@@ -321,6 +325,12 @@
             displayId = sourceDisplayId;
         }
 
+        if (displayId == INVALID_DISPLAY && options != null) {
+            final int callerDisplayId = options.getCallerDisplayId();
+            if (DEBUG) appendLog("display-from-caller=" + callerDisplayId);
+            displayId = callerDisplayId;
+        }
+
         if (displayId != INVALID_DISPLAY
                 && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) == null) {
             displayId = currentParams.mPreferredDisplayId;
diff --git a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
index 1a57168..143543e 100644
--- a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
+++ b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
@@ -28,7 +28,7 @@
  * Class used by {@link RecentsAnimationController} to create a surface control with taking
  * screenshot of task when canceling recents animation.
  *
- * @see {@link RecentsAnimationController#cancelOnNextTransitionStart}
+ * @see {@link RecentsAnimationController#setCancelOnNextTransitionStart}
  */
 class TaskScreenshotAnimatable implements SurfaceAnimator.Animatable {
     private static final String TAG = "TaskScreenshotAnim";
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index eb45e73..79367a0 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -321,7 +321,9 @@
      */
     private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds,
             boolean toFullscreen) {
-        mBoundsAnimatingRequested = true;
+        if (mAnimationType == BoundsAnimationController.BOUNDS) {
+            mBoundsAnimatingRequested = true;
+        }
         mBoundsAnimatingToFullscreen = toFullscreen;
         if (destBounds != null) {
             mBoundsAnimationTarget.set(destBounds);
@@ -1586,8 +1588,10 @@
                 return false;
             }
 
-            mBoundsAnimatingRequested = false;
-            mBoundsAnimating = true;
+            if (animationType == BoundsAnimationController.BOUNDS) {
+                mBoundsAnimatingRequested = false;
+                mBoundsAnimating = true;
+            }
             mAnimationType = animationType;
 
             // If we are changing UI mode, as in the PiP to fullscreen
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 636f4e6..05cfbd4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -511,4 +511,9 @@
      */
     public abstract void removeNonHighRefreshRatePackage(@NonNull String packageName);
 
+    /**
+     * Checks if this display is touchable.
+     */
+    public abstract boolean isTouchableDisplay(int displayId);
+
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 70e446c..fbdc54a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7455,6 +7455,17 @@
                         .removeNonHighRefreshRatePackage(packageName));
             }
         }
+
+        @Override
+        public boolean isTouchableDisplay(int displayId) {
+            synchronized (mGlobalLock) {
+                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+                final Configuration configuration =
+                        displayContent != null ? displayContent.getConfiguration() : null;
+                return configuration != null
+                        && configuration.touchscreen == Configuration.TOUCHSCREEN_FINGER;
+            }
+        }
     }
 
     void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d5a736d..d90e66e 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -370,6 +370,13 @@
             // of the proper size. The preserved surface will still be removed when client
             // finishes drawing to the new surface.
             mSurfaceDestroyDeferred = false;
+
+            // Make sure to reparent any children of the new surface back to the preserved
+            // surface before destroying it.
+            if (mSurfaceController != null && mPendingDestroySurface != null) {
+                mPostDrawTransaction.reparentChildren(mSurfaceController.mSurfaceControl,
+                        mPendingDestroySurface.mSurfaceControl).apply();
+            }
             destroySurfaceLocked();
             mSurfaceDestroyDeferred = true;
             return;
@@ -729,12 +736,7 @@
             if (!mService.mLimitedAlphaCompositing
                     || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
                     || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)))) {
-                //Slog.i(TAG_WM, "Applying alpha transform");
-                if (screenAnimation) {
-                    mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha();
-                }
-            } else {
-                //Slog.i(TAG_WM, "Not applying alpha transform");
+                mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha();
             }
 
             if ((DEBUG_ANIM || WindowManagerService.localLOGV)
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 1c8c46c..466ca93 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -41,7 +41,6 @@
 #include <utils/Looper.h>
 #include <utils/threads.h>
 #include <utils/Trace.h>
-#include <utils/SortedVector.h>
 
 #include <binder/IServiceManager.h>
 
@@ -307,7 +306,7 @@
         wp<PointerController> pointerController;
 
         // Input devices to be disabled
-        SortedVector<int32_t> disabledInputDevices;
+        std::set<int32_t> disabledInputDevices;
 
         // Associated Pointer controller display.
         int32_t pointerDisplayId;
@@ -898,13 +897,13 @@
     { // acquire lock
         AutoMutex _l(mLock);
 
-        ssize_t index = mLocked.disabledInputDevices.indexOf(deviceId);
-        bool currentlyEnabled = index < 0;
+        auto it = mLocked.disabledInputDevices.find(deviceId);
+        bool currentlyEnabled = it == mLocked.disabledInputDevices.end();
         if (!enabled && currentlyEnabled) {
-            mLocked.disabledInputDevices.add(deviceId);
+            mLocked.disabledInputDevices.insert(deviceId);
         }
         if (enabled && !currentlyEnabled) {
-            mLocked.disabledInputDevices.remove(deviceId);
+            mLocked.disabledInputDevices.erase(deviceId);
         }
     } // release lock
 
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index 98e4343..8b2cbbd 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -4,3 +4,11 @@
     api_dir: "schema",
     package_name: "com.android.server.pm.permission.configfile",
 }
+
+
+xsd_config {
+    name: "platform-compat-config",
+    srcs: ["platform-compat-config.xsd"],
+    api_dir: "platform-compat-schema",
+    package_name: "com.android.server.compat.config",
+}
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd
new file mode 100644
index 0000000..ee39e50
--- /dev/null
+++ b/services/core/xsd/platform-compat-config.xsd
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- This defines the format of the XML file generated by
+  ~ com.android.compat.annotation.ChangeIdProcessor annotation processor (from
+  ~ tools/platform-compat), and is parsed in com/android/server/compat/CompatConfig.java.
+-->
+<xs:schema version="2.0" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+    <xs:complexType name="change">
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute type="xs:long" name="id" use="required"/>
+                <xs:attribute type="xs:string" name="name" use="required"/>
+                <xs:attribute type="xs:boolean" name="disabled"/>
+                <xs:attribute type="xs:int" name="enableAfterTargetSdk"/>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+
+    <xs:element name="config">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="compat-change" type="change" maxOccurs="unbounded"
+                            minOccurs="0"/>
+            </xs:sequence>
+        </xs:complexType>
+        <xs:unique name="UniqueId">
+            <xs:selector xpath="compat-change" />
+            <xs:field xpath="@id" />
+        </xs:unique>
+    </xs:element>
+</xs:schema>
+
+
+
+
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt
new file mode 100644
index 0000000..8456785
--- /dev/null
+++ b/services/core/xsd/platform-compat-schema/current.txt
@@ -0,0 +1,31 @@
+// Signature format: 2.0
+package com.android.server.compat.config {
+
+  public class Change {
+    ctor public Change();
+    method public boolean getDisabled();
+    method public int getEnableAfterTargetSdk();
+    method public long getId();
+    method public String getName();
+    method public String getValue();
+    method public void setDisabled(boolean);
+    method public void setEnableAfterTargetSdk(int);
+    method public void setId(long);
+    method public void setName(String);
+    method public void setValue(String);
+  }
+
+  public class Config {
+    ctor public Config();
+    method public java.util.List<com.android.server.compat.config.Change> getCompatChange();
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method public static com.android.server.compat.config.Config read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat-schema/last_current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/platform-compat-schema/last_current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat-schema/last_removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/core/xsd/platform-compat-schema/last_removed.txt
diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat-schema/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/services/core/xsd/platform-compat-schema/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a04875f..e198c79 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -87,6 +87,7 @@
 import com.android.server.broadcastradio.BroadcastRadioService;
 import com.android.server.camera.CameraServiceProxy;
 import com.android.server.clipboard.ClipboardService;
+import com.android.server.compat.PlatformCompat;
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.coverage.CoverageService;
@@ -1098,6 +1099,11 @@
             t.traceBegin("SignedConfigService");
             SignedConfigService.registerUpdateReceiver(mSystemContext);
             t.traceEnd();
+
+            t.traceBegin("PlatformCompat");
+            ServiceManager.addService("platform_compat", new PlatformCompat(context));
+            t.traceEnd();
+
         } catch (RuntimeException e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service", e);
@@ -1974,6 +1980,11 @@
         }
         t.traceEnd();
 
+        // Permission policy service
+        t.traceBegin("StartPermissionPolicyService");
+        mSystemServiceManager.startService(PermissionPolicyService.class);
+        t.traceEnd();
+
         t.traceBegin("MakePackageManagerServiceReady");
         mPackageManagerService.systemReady();
         t.traceEnd();
@@ -2008,11 +2019,6 @@
         mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY);
         t.traceEnd();
 
-        // Permission policy service
-        t.traceBegin("StartPermissionPolicyService");
-        mSystemServiceManager.startService(PermissionPolicyService.class);
-        t.traceEnd();
-
         // These are needed to propagate to the runnable below.
         final NetworkManagementService networkManagementF = networkManagement;
         final NetworkStatsService networkStatsF = networkStats;
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
new file mode 100644
index 0000000..3ce514a
--- /dev/null
+++ b/services/robotests/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//##################################################################
+// FrameworksServicesLib app just for Robolectric test target      #
+//##################################################################
+
+android_app {
+    name: "FrameworksServicesLib",
+    platform_apis: true,
+
+    privileged: true,
+
+    static_libs: [
+        "services.core",
+        "services.net",
+    ],
+}
+
+//##################################################################
+// FrameworksServicesLib Robolectric test target.                  #
+//##################################################################
+android_robolectric_test {
+    name: "FrameworksServicesRoboTests",
+
+    srcs: ["src/**/*.java"],
+
+    java_resource_dirs: ["config"],
+
+    // Include the testing libraries
+    libs: [
+        "platform-test-annotations",
+        "testng",
+    ],
+
+    instrumentation_for: "FrameworksServicesLib",
+}
+
+filegroup {
+    name: "FrameworksServicesRoboShadows",
+    srcs: ["src/com/android/server/testing/shadows/**/*.java"],
+}
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
deleted file mode 100644
index 0cf0d34..0000000
--- a/services/robotests/Android.mk
+++ /dev/null
@@ -1,89 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-###################################################################
-# FrameworksServicesLib app just for Robolectric test target      #
-###################################################################
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := FrameworksServicesLib
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    services.core \
-    services.net
-
-include $(BUILD_PACKAGE)
-
-###################################################################
-# FrameworksServicesLib Robolectric test target.                  #
-###################################################################
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := FrameworksServicesRoboTests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := \
-    $(LOCAL_PATH)/res
-
-LOCAL_JAVA_RESOURCE_DIRS := config
-
-# Include the testing libraries
-LOCAL_JAVA_LIBRARIES := \
-    platform-test-annotations \
-    robolectric_android-all-stub \
-    Robolectric_all-target \
-    mockito-robolectric-prebuilt \
-    truth-prebuilt \
-    testng
-
-LOCAL_INSTRUMENTATION_FOR := FrameworksServicesLib
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-###################################################################
-# FrameworksServicesLib runner target to run the previous target. #
-###################################################################
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := RunFrameworksServicesRoboTests
-
-LOCAL_JAVA_LIBRARIES := \
-    FrameworksServicesRoboTests \
-    platform-test-annotations \
-    robolectric_android-all-stub \
-    Robolectric_all-target \
-    mockito-robolectric-prebuilt \
-    truth-prebuilt \
-    testng
-
-LOCAL_TEST_PACKAGE := FrameworksServicesLib
-
-LOCAL_ROBOTEST_FILES := $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.)
-
-include external/robolectric-shadows/run_robotests.mk
-
-###################################################################
-# include subdir Android.mk files
-###################################################################
-include $(CLEAR_VARS)
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/services/robotests/AndroidManifest.xml b/services/robotests/AndroidManifest.xml
new file mode 100644
index 0000000..828c8fa
--- /dev/null
+++ b/services/robotests/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          coreApp="true"
+          package="com.android.server.robotests">
+
+    <application/>
+
+</manifest>
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
new file mode 100644
index 0000000..9d384e9
--- /dev/null
+++ b/services/robotests/backup/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//##################################################################
+// BackupFrameworksServicesLib app just for Robolectric test target      #
+//##################################################################
+android_app {
+    name: "BackupFrameworksServicesLib",
+    platform_apis: true,
+
+    privileged: true,
+
+    static_libs: [
+        "bmgr",
+        "bu",
+        "services.backup",
+        "services.core",
+        "services.net",
+    ],
+}
+
+//##################################################################
+// BackupFrameworksServicesLib Robolectric test target.                  #
+//##################################################################
+android_robolectric_test {
+    name: "BackupFrameworksServicesRoboTests",
+    srcs: [
+        "src/**/*.java",
+        ":FrameworksServicesRoboShadows",
+    ],
+
+    java_resource_dirs: ["config"],
+
+    // Include the testing libraries
+    libs: [
+        "platform-test-annotations",
+        "testng",
+    ],
+
+    instrumentation_for: "BackupFrameworksServicesLib",
+
+}
diff --git a/services/robotests/backup/Android.mk b/services/robotests/backup/Android.mk
deleted file mode 100644
index bd4ebbd..0000000
--- a/services/robotests/backup/Android.mk
+++ /dev/null
@@ -1,84 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-###################################################################
-# BackupFrameworksServicesLib app just for Robolectric test target      #
-###################################################################
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := BackupFrameworksServicesLib
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    bmgr \
-    bu \
-    services.backup \
-    services.core \
-    services.net
-
-include $(BUILD_PACKAGE)
-
-###################################################################
-# BackupFrameworksServicesLib Robolectric test target.                  #
-###################################################################
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := BackupFrameworksServicesRoboTests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-    $(call all-java-files-under, ../src/com/android/server/testing/shadows)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_JAVA_RESOURCE_DIRS := config
-
-# Include the testing libraries
-LOCAL_JAVA_LIBRARIES := \
-    platform-test-annotations \
-    robolectric_android-all-stub \
-    Robolectric_all-target \
-    mockito-robolectric-prebuilt \
-    truth-prebuilt \
-    testng
-
-LOCAL_INSTRUMENTATION_FOR := BackupFrameworksServicesLib
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-###################################################################
-# BackupFrameworksServicesLib runner target to run the previous target. #
-###################################################################
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := RunBackupFrameworksServicesRoboTests
-
-LOCAL_JAVA_LIBRARIES := \
-    BackupFrameworksServicesRoboTests \
-    platform-test-annotations \
-    robolectric_android-all-stub \
-    Robolectric_all-target \
-    mockito-robolectric-prebuilt \
-    truth-prebuilt \
-    testng
-
-LOCAL_TEST_PACKAGE := BackupFrameworksServicesLib
-
-include external/robolectric-shadows/run_robotests.mk
diff --git a/services/robotests/backup/AndroidManifest.xml b/services/robotests/backup/AndroidManifest.xml
new file mode 100644
index 0000000..0932378
--- /dev/null
+++ b/services/robotests/backup/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          coreApp="true"
+          package="com.android.server.backup.robotests">
+
+    <application/>
+
+</manifest>
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 06c83a2..6feac52 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -53,6 +53,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.longThat;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -129,9 +130,12 @@
         ConnectivityService connectivityService;
         LocationManager locationManager;
         ConstraintController constraintController;
+        // Freeze time for testing.
+        long nowElapsed;
 
         InjectorForTest(Context ctx) {
             super(ctx);
+            nowElapsed = SystemClock.elapsedRealtime();
         }
 
         @Override
@@ -156,6 +160,11 @@
         }
 
         @Override
+        long getElapsedRealtime() {
+            return nowElapsed;
+        }
+
+        @Override
         LocationManager getLocationManager() {
             return locationManager;
         }
@@ -494,11 +503,44 @@
         mDeviceIdleController.becomeActiveLocked("testing", 0);
         verifyStateConditions(STATE_ACTIVE);
 
+        setAlarmSoon(false);
         setChargingOn(false);
         setScreenOn(false);
 
         mDeviceIdleController.becomeInactiveIfAppropriateLocked();
         verifyStateConditions(STATE_INACTIVE);
+        verify(mDeviceIdleController)
+                .scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT), eq(false));
+    }
+
+    @Test
+    public void testStateActiveToStateInactive_UpcomingAlarm() {
+        final long timeUntilAlarm = mConstants.MIN_TIME_TO_ALARM / 2;
+        // Set an upcoming alarm that will prevent full idle.
+        doReturn(mInjector.getElapsedRealtime() + timeUntilAlarm)
+                .when(mAlarmManager).getNextWakeFromIdleTime();
+
+        InOrder inOrder = inOrder(mDeviceIdleController);
+
+        enterDeepState(STATE_ACTIVE);
+        setQuickDozeEnabled(false);
+        setChargingOn(false);
+        setScreenOn(false);
+
+        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+        verifyStateConditions(STATE_INACTIVE);
+        inOrder.verify(mDeviceIdleController)
+                .scheduleAlarmLocked(eq(timeUntilAlarm + mConstants.INACTIVE_TIMEOUT), eq(false));
+
+        enterDeepState(STATE_ACTIVE);
+        setQuickDozeEnabled(true);
+        setChargingOn(false);
+        setScreenOn(false);
+
+        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+        inOrder.verify(mDeviceIdleController).scheduleAlarmLocked(
+                eq(timeUntilAlarm + mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
     }
 
     @Test
@@ -515,42 +557,68 @@
 
     @Test
     public void testTransitionFromAnyStateToStateQuickDozeDelay() {
+        setAlarmSoon(false);
+        InOrder inOrder = inOrder(mDeviceIdleController);
+
         enterDeepState(STATE_ACTIVE);
         setQuickDozeEnabled(true);
         setChargingOn(false);
         setScreenOn(false);
         verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+        inOrder.verify(mDeviceIdleController)
+                .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
 
         enterDeepState(STATE_INACTIVE);
         setQuickDozeEnabled(true);
         verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+        inOrder.verify(mDeviceIdleController)
+                .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
 
         enterDeepState(STATE_IDLE_PENDING);
         setQuickDozeEnabled(true);
         verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+        inOrder.verify(mDeviceIdleController)
+                .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
 
         enterDeepState(STATE_SENSING);
         setQuickDozeEnabled(true);
         verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+        inOrder.verify(mDeviceIdleController)
+                .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
 
         enterDeepState(STATE_LOCATING);
         setQuickDozeEnabled(true);
         verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+        inOrder.verify(mDeviceIdleController)
+                .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false));
 
         // IDLE should stay as IDLE.
         enterDeepState(STATE_IDLE);
+        // Clear out any alarm setting from the order before checking for this section.
+        inOrder.verify(mDeviceIdleController, atLeastOnce())
+                .scheduleAlarmLocked(anyLong(), anyBoolean());
         setQuickDozeEnabled(true);
         verifyStateConditions(STATE_IDLE);
+        inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean());
 
         // IDLE_MAINTENANCE should stay as IDLE_MAINTENANCE.
         enterDeepState(STATE_IDLE_MAINTENANCE);
+        // Clear out any alarm setting from the order before checking for this section.
+        inOrder.verify(mDeviceIdleController, atLeastOnce())
+                .scheduleAlarmLocked(anyLong(), anyBoolean());
         setQuickDozeEnabled(true);
         verifyStateConditions(STATE_IDLE_MAINTENANCE);
+        inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean());
 
+        // State is already QUICK_DOZE_DELAY. No work should be done.
         enterDeepState(STATE_QUICK_DOZE_DELAY);
+        // Clear out any alarm setting from the order before checking for this section.
+        inOrder.verify(mDeviceIdleController, atLeastOnce())
+                .scheduleAlarmLocked(anyLong(), anyBoolean());
         setQuickDozeEnabled(true);
         mDeviceIdleController.becomeInactiveIfAppropriateLocked();
         verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+        inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean());
     }
 
     @Test
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 869913d..0614b3a 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -39,6 +39,7 @@
         "platformprotosnano",
         "hamcrest-library",
         "servicestests-utils",
+        "xml-writer-device-lib",
     ],
 
     aidl: {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java
index aad7230..cdcc338 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.accessibility;
 
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -174,6 +175,7 @@
         }
 
         // Check that correct gesture was recognized.
-        verify(mResultListener).onGestureCompleted(gestureId);
+        verify(mResultListener).onGestureCompleted(
+                argThat(gestureInfo -> gestureInfo.getGestureId() == gestureId));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index ba2959f..2f9f9bb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -23,21 +23,25 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.view.Display;
 
 import com.android.server.wm.WindowManagerInternal;
 
@@ -50,6 +54,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 
 
 /**
@@ -73,6 +78,9 @@
     @Mock GlobalActionPerformer mMockGlobalActionPerformer;
     @Mock KeyEventDispatcher mMockKeyEventDispatcher;
     @Mock MagnificationController mMockMagnificationController;
+    @Mock IBinder mMockIBinder;
+    @Mock IAccessibilityServiceClient mMockServiceClient;
+    @Mock MotionEventInjector mMockMotionEventInjector;
 
     MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
 
@@ -82,15 +90,22 @@
         when(mMockSystemSupport.getKeyEventDispatcher()).thenReturn(mMockKeyEventDispatcher);
         when(mMockSystemSupport.getMagnificationController())
                 .thenReturn(mMockMagnificationController);
+        when(mMockSystemSupport.getMotionEventInjectorForDisplayLocked(
+                Display.DEFAULT_DISPLAY)).thenReturn(mMockMotionEventInjector);
 
         when(mMockServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
         mMockResolveInfo.serviceInfo = mock(ServiceInfo.class);
         mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
 
+        when(mMockIBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient);
+        when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn(
+                true);
+
         mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
                 COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
                 mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal,
                 mMockGlobalActionPerformer, mMockA11yWindowManager);
+        when(mMockSecurityPolicy.canPerformGestures(mConnection)).thenReturn(true);
     }
 
     @After
@@ -115,25 +130,23 @@
 
     @Test
     public void bindConnectUnbind_linksAndUnlinksToServiceDeath() throws RemoteException {
-        IBinder mockBinder = mock(IBinder.class);
         setServiceBinding(COMPONENT_NAME);
         mConnection.bindLocked();
-        mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
-        verify(mockBinder).linkToDeath(eq(mConnection), anyInt());
+        mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
+        verify(mMockIBinder).linkToDeath(eq(mConnection), anyInt());
         mConnection.unbindLocked();
-        verify(mockBinder).unlinkToDeath(eq(mConnection), anyInt());
+        verify(mMockIBinder).unlinkToDeath(eq(mConnection), anyInt());
     }
 
     @Test
     public void connectedServiceCrashedAndRestarted_crashReportedInServiceInfo() {
-        IBinder mockBinder = mock(IBinder.class);
         setServiceBinding(COMPONENT_NAME);
         mConnection.bindLocked();
-        mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
+        mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
         assertFalse(mConnection.getServiceInfo().crashed);
         mConnection.binderDied();
         assertTrue(mConnection.getServiceInfo().crashed);
-        mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
+        mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
         mHandler.sendAllMessages();
         assertFalse(mConnection.getServiceInfo().crashed);
     }
@@ -145,10 +158,9 @@
 
     @Test
     public void binderDied_keysGetFlushed() {
-        IBinder mockBinder = mock(IBinder.class);
         setServiceBinding(COMPONENT_NAME);
         mConnection.bindLocked();
-        mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
+        mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
         mConnection.binderDied();
         assertTrue(mConnection.getServiceInfo().crashed);
         verify(mMockKeyEventDispatcher).flush(mConnection);
@@ -157,17 +169,63 @@
     @Test
     public void connectedService_notInEnabledServiceList_doNotInitClient()
             throws RemoteException {
-        IBinder mockBinder = mock(IBinder.class);
-        IAccessibilityServiceClient mockClient = mock(IAccessibilityServiceClient.class);
-        when(mockBinder.queryLocalInterface(any())).thenReturn(mockClient);
         when(mMockUserState.getEnabledServicesLocked())
                 .thenReturn(Collections.emptySet());
         setServiceBinding(COMPONENT_NAME);
 
         mConnection.bindLocked();
-        mConnection.onServiceConnected(COMPONENT_NAME, mockBinder);
+        mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
         mHandler.sendAllMessages();
         verify(mMockSystemSupport, times(2)).onClientChangeLocked(false);
-        verify(mockClient, times(0)).init(any(), anyInt(), any());
+        verify(mMockServiceClient, times(0)).init(any(), anyInt(), any());
     }
+
+    @Test
+    public void sendGesture_touchableDisplay_injectEvents()
+            throws RemoteException {
+        setServiceBinding(COMPONENT_NAME);
+        mConnection.bindLocked();
+        mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
+
+        ParceledListSlice parceledListSlice = mock(ParceledListSlice.class);
+        List<GestureDescription.GestureStep> gestureSteps = mock(List.class);
+        when(parceledListSlice.getList()).thenReturn(gestureSteps);
+        mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY);
+
+        verify(mMockMotionEventInjector).injectEvents(gestureSteps, mMockServiceClient, 0);
+    }
+
+    @Test
+    public void sendGesture_untouchableDisplay_performGestureResultFailed()
+            throws RemoteException {
+        when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn(
+                false);
+        setServiceBinding(COMPONENT_NAME);
+        mConnection.bindLocked();
+        mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
+
+        ParceledListSlice parceledListSlice = mock(ParceledListSlice.class);
+        List<GestureDescription.GestureStep> gestureSteps = mock(List.class);
+        when(parceledListSlice.getList()).thenReturn(gestureSteps);
+        mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY);
+
+        verify(mMockMotionEventInjector, never()).injectEvents(gestureSteps, mMockServiceClient, 0);
+        verify(mMockServiceClient).onPerformGestureResult(0, false);
+    }
+
+    @Test
+    public void sendGesture_invalidDisplay_performGestureResultFailed()
+            throws RemoteException {
+        setServiceBinding(COMPONENT_NAME);
+        mConnection.bindLocked();
+        mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
+
+        ParceledListSlice parceledListSlice = mock(ParceledListSlice.class);
+        List<GestureDescription.GestureStep> gestureSteps = mock(List.class);
+        when(parceledListSlice.getList()).thenReturn(gestureSteps);
+        mConnection.dispatchGesture(0, parceledListSlice, Display.INVALID_DISPLAY);
+
+        verify(mMockServiceClient).onPerformGestureResult(0, false);
+    }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index d008ca6..f3c5e99 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -22,9 +22,17 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compat.annotation.Change;
+import com.android.compat.annotation.XmlWriter;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.UUID;
+
 @RunWith(AndroidJUnit4.class)
 public class CompatConfigTest {
 
@@ -35,6 +43,27 @@
         return ai;
     }
 
+    private File createTempDir() {
+        String base = System.getProperty("java.io.tmpdir");
+        File dir = new File(base, UUID.randomUUID().toString());
+        assertThat(dir.mkdirs()).isTrue();
+        return dir;
+    }
+
+    private void writeChangesToFile(Change[] changes, File f) {
+        XmlWriter writer = new XmlWriter();
+        for (Change change: changes) {
+            writer.addChange(change);
+        }
+        try {
+            f.createNewFile();
+            writer.write(new FileOutputStream(f));
+        } catch (IOException e) {
+            throw new RuntimeException(
+                    "Encountered an error while writing compat config file", e);
+        }
+    }
+
     @Test
     public void testUnknownChangeEnabled() {
         CompatConfig pc = new CompatConfig();
@@ -170,4 +199,45 @@
         sysApp.flags |= ApplicationInfo.FLAG_SYSTEM;
         assertThat(pc.isChangeEnabled(1234L, sysApp)).isTrue();
     }
+
+    @Test
+    public void testReadConfig() {
+        Change[] changes = {new Change(1234L, "MY_CHANGE1", false, 2), new Change(1235L,
+                "MY_CHANGE2", true, null), new Change(1236L, "MY_CHANGE3", false, null)};
+
+        File dir = createTempDir();
+        writeChangesToFile(changes, new File(dir.getPath() + "/platform_compat_config.xml"));
+
+        CompatConfig pc = new CompatConfig();
+        pc.initConfigFromLib(dir);
+
+        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
+        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
+        assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse();
+        assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue();
+    }
+
+    @Test
+    public void testReadConfigMultipleFiles() {
+        Change[] changes1 = {new Change(1234L, "MY_CHANGE1", false, 2)};
+        Change[] changes2 = {new Change(1235L, "MY_CHANGE2", true, null), new Change(1236L,
+                "MY_CHANGE3", false, null)};
+
+        File dir = createTempDir();
+        writeChangesToFile(changes1,
+                new File(dir.getPath() + "/libcore_platform_compat_config.xml"));
+        writeChangesToFile(changes2,
+                new File(dir.getPath() + "/frameworks_platform_compat_config.xml"));
+
+
+        CompatConfig pc = new CompatConfig();
+        pc.initConfigFromLib(dir);
+
+        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
+        assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
+        assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse();
+        assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue();
+    }
 }
+
+
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
new file mode 100644
index 0000000..65f9e32
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -0,0 +1,340 @@
+/*
+ * 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.pm;
+
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.os.Build;
+import android.os.Process;
+import android.permission.IPermissionManager;
+import android.util.ArrayMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Map;
+
+@RunWith(JUnit4.class)
+public class AppsFilterTest {
+
+    private static final int DUMMY_CALLING_UID = 10345;
+
+    @Mock
+    IPermissionManager mPermissionManagerMock;
+
+    @Mock
+    AppsFilter.ConfigProvider mConfigProviderMock;
+
+    @Mock
+    AppOpsManager mAppOpsManager;
+
+    private Map<String, PackageParser.Package> mExisting = new ArrayMap<>();
+
+    private static PackageBuilder pkg(String packageName) {
+        return new PackageBuilder(packageName)
+                .setApplicationInfoTargetSdkVersion(Build.VERSION_CODES.R);
+    }
+
+    private static PackageBuilder pkg(String packageName, Intent... queries) {
+        return pkg(packageName).setQueriesIntents(queries);
+    }
+
+    private static PackageBuilder pkg(String packageName, String... queriesPackages) {
+        return pkg(packageName).setQueriesPackages(queriesPackages);
+    }
+
+    private static PackageBuilder pkg(String packageName, IntentFilter... filters) {
+        final PackageBuilder packageBuilder = pkg(packageName).addActivity(
+                pkg -> new PackageParser.ParseComponentArgs(pkg, new String[1], 0, 0, 0, 0, 0, 0,
+                        new String[]{packageName}, 0, 0, 0), new ActivityInfo());
+        for (IntentFilter filter : filters) {
+            packageBuilder.addActivityIntentInfo(0 /* index */, activity -> {
+                final PackageParser.ActivityIntentInfo info =
+                        new PackageParser.ActivityIntentInfo(activity);
+                if (filter.countActions() > 0) {
+                    filter.actionsIterator().forEachRemaining(info::addAction);
+                }
+                if (filter.countCategories() > 0) {
+                    filter.actionsIterator().forEachRemaining(info::addAction);
+                }
+                if (filter.countDataAuthorities() > 0) {
+                    filter.authoritiesIterator().forEachRemaining(info::addDataAuthority);
+                }
+                if (filter.countDataSchemes() > 0) {
+                    filter.schemesIterator().forEachRemaining(info::addDataScheme);
+                }
+                return info;
+            });
+        }
+        return packageBuilder;
+    }
+
+    @Before
+    public void setup() throws Exception {
+        mExisting = new ArrayMap<>();
+
+        MockitoAnnotations.initMocks(this);
+        when(mPermissionManagerMock
+                .checkPermission(anyString(), anyString(), anyInt()))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mConfigProviderMock.isEnabled()).thenReturn(true);
+        when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq(
+                DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_DEFAULT);
+    }
+
+    @Test
+    public void testQueriesAction_FilterMatches() {
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{}, false);
+
+        PackageSetting target = simulateAddPackage(appsFilter,
+                pkg("com.some.package", new IntentFilter("TEST_ACTION"))).build();
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package", new Intent("TEST_ACTION"))).build();
+
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+    }
+
+    @Test
+    public void testQueriesAction_NoMatchingAction_Filters() {
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{}, false);
+
+        PackageSetting target = simulateAddPackage(appsFilter,
+                pkg("com.some.package")).build();
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package", new Intent("TEST_ACTION"))).build();
+
+        assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+    }
+
+    @Test
+    public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() {
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{}, false);
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+        PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package",
+                new Intent("TEST_ACTION")).setApplicationInfoTargetSdkVersion(
+                Build.VERSION_CODES.P)).build();
+
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+    }
+
+    @Test
+    public void testNoQueries_Filters() {
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{}, false);
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package")).build();
+
+        assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+    }
+
+    @Test
+    public void testForceQueryable_DoesntFilter() {
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{}, false);
+
+        PackageSetting target =
+                simulateAddPackage(appsFilter, pkg("com.some.package").setForceQueryable(true))
+                        .build();
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package")).build();
+
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+    }
+
+    @Test
+    public void testForceQueryableByDevice_SystemCaller_DoesntFilter() {
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{"com.some.package"}, false);
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"))
+                .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
+                .build();
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package")).build();
+
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+    }
+
+    @Test
+    public void testForceQueryableByDevice_NonSystemCaller_Filters() {
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{"com.some.package"}, false);
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package")).build();
+
+        assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+    }
+
+
+    @Test
+    public void testSystemQueryable_DoesntFilter() {
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{}, true /* system force queryable */);
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"))
+                .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
+                .build();
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package")).build();
+
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+    }
+
+    @Test
+    public void testQueriesPackage_DoesntFilter() {
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{}, false);
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package", "com.some.package")).build();
+
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+    }
+
+    @Test
+    public void testNoQueries_AppOpModeDeny_Filters() {
+        when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq(
+                DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_ERRORED);
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{}, false);
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package")).build();
+
+        assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+    }
+
+    @Test
+    public void testNoQueries_AppOpModeAllow_DoesntFilter() {
+        when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq(
+                DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_ALLOWED);
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{}, false);
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package")).build();
+
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+    }
+
+    @Test
+    public void testNoQueries_AppOpModeIgnore_Filters() {
+        when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq(
+                DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_IGNORED);
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{}, false);
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package")).build();
+
+        assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+    }
+
+    @Test
+    public void testNoQueries_FeatureOff_DoesntFilter() {
+        when(mConfigProviderMock.isEnabled()).thenReturn(false);
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{}, false);
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+        PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package")).build();
+
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+    }
+
+    @Test
+    public void testSystemUid_DoesntFilter() {
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{}, false);
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+
+        assertFalse(appsFilter.shouldFilterApplication(0, null, target, 0));
+        assertFalse(appsFilter.shouldFilterApplication(
+                Process.FIRST_APPLICATION_UID - 1, null, target, 0));
+    }
+
+    @Test
+    public void testNonSystemUid_NoCallingSetting_Filters() {
+        final AppsFilter appsFilter =
+                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                        new String[]{}, false);
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
+
+        assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, null, target, 0));
+    }
+
+    private PackageSettingBuilder simulateAddPackage(AppsFilter filter,
+            PackageBuilder newPkgBuilder) {
+        PackageParser.Package newPkg = newPkgBuilder.build();
+        filter.addPackage(newPkg, mExisting);
+        mExisting.put(newPkg.packageName, newPkg);
+        return new PackageSettingBuilder()
+                .setPackage(newPkg)
+                .setName(newPkg.packageName)
+                .setCodePath("/")
+                .setResourcePath("/")
+                .setPVersionCode(1L);
+    }
+
+}
+
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java
new file mode 100644
index 0000000..c38672c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java
@@ -0,0 +1,176 @@
+/*
+ * 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.pm;
+
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageParser;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+class PackageBuilder {
+    final PackageParser.Package mPkg;
+
+    PackageBuilder(String packageName) {
+        mPkg = new PackageParser.Package(packageName);
+    }
+
+    PackageBuilder setApplicationInfoCodePath(String codePath) {
+        mPkg.applicationInfo.setCodePath(codePath);
+        return this;
+    }
+
+    PackageBuilder setApplicationInfoResourcePath(String resourcePath) {
+        mPkg.applicationInfo.setResourcePath(resourcePath);
+        return this;
+    }
+
+    PackageBuilder setCodePath(String codePath) {
+        mPkg.codePath = codePath;
+        return this;
+    }
+
+    PackageBuilder setBaseCodePath(String baseCodePath) {
+        mPkg.baseCodePath = baseCodePath;
+        return this;
+    }
+
+    PackageBuilder addUsesStaticLibrary(String name, long version) {
+        mPkg.usesStaticLibraries = ArrayUtils.add(mPkg.usesStaticLibraries, name);
+        mPkg.usesStaticLibrariesVersions =
+                ArrayUtils.appendLong(mPkg.usesStaticLibrariesVersions, version);
+        return this;
+    }
+
+    PackageBuilder setApplicationInfoNativeLibraryRootDir(String dir) {
+        mPkg.applicationInfo.nativeLibraryRootDir = dir;
+        return this;
+    }
+
+    PackageBuilder setStaticSharedLib(String staticSharedLibName, long staticSharedLibVersion) {
+        mPkg.staticSharedLibVersion = staticSharedLibVersion;
+        mPkg.staticSharedLibName = staticSharedLibName;
+        return this;
+    }
+
+    PackageBuilder setManifestPackageName(String manifestPackageName) {
+        mPkg.manifestPackageName = manifestPackageName;
+        return this;
+    }
+
+    PackageBuilder setVersionCodeMajor(int versionCodeMajor) {
+        mPkg.mVersionCodeMajor = versionCodeMajor;
+        return this;
+    }
+
+    PackageBuilder setVersionCode(int versionCode) {
+        mPkg.mVersionCode = versionCode;
+        return this;
+    }
+
+    PackageBuilder addSplitCodePath(String splitCodePath) {
+        mPkg.splitCodePaths =
+                ArrayUtils.appendElement(String.class, mPkg.splitCodePaths, splitCodePath);
+        return this;
+    }
+
+    PackageBuilder setApplicationInfoVolumeUuid(String volumeUuid) {
+        mPkg.applicationInfo.volumeUuid = volumeUuid;
+        return this;
+    }
+
+    PackageBuilder addLibraryName(String libraryName) {
+        mPkg.libraryNames = ArrayUtils.add(mPkg.libraryNames, libraryName);
+        return this;
+    }
+
+    PackageBuilder setRealPackageName(String realPackageName) {
+        mPkg.mRealPackage = realPackageName;
+        return this;
+    }
+
+    PackageBuilder setCpuAbiOVerride(String cpuAbiOverride) {
+        mPkg.cpuAbiOverride = cpuAbiOverride;
+        return this;
+    }
+
+    PackageBuilder addPermissionRequest(String permissionName) {
+        mPkg.requestedPermissions.add(permissionName);
+        return this;
+    }
+
+    PackageParser.Package build() {
+        return mPkg;
+    }
+
+    public PackageBuilder addApplicationInfoFlag(int flag) {
+        mPkg.applicationInfo.flags |= flag;
+        return this;
+    }
+
+    public PackageBuilder setApplicationInfoTargetSdkVersion(int versionCode) {
+        mPkg.applicationInfo.targetSdkVersion = versionCode;
+        return this;
+    }
+
+    public PackageBuilder setQueriesIntents(Collection<Intent> queriesIntents) {
+        mPkg.mQueriesIntents = new ArrayList<>(queriesIntents);
+        return this;
+    }
+
+    public PackageBuilder setQueriesIntents(Intent... intents) {
+        return setQueriesIntents(Arrays.asList(intents));
+    }
+
+    public PackageBuilder setQueriesPackages(Collection<String> queriesPackages) {
+        mPkg.mQueriesPackages = new ArrayList<>(queriesPackages);
+        return this;
+    }
+
+    public PackageBuilder setQueriesPackages(String... queriesPackages) {
+        return setQueriesPackages(Arrays.asList(queriesPackages));
+    }
+
+    public PackageBuilder setForceQueryable(boolean forceQueryable) {
+        mPkg.mForceQueryable = forceQueryable;
+        return this;
+    }
+
+    public interface ParseComponentArgsCreator {
+        PackageParser.ParseComponentArgs create(PackageParser.Package pkg);
+    }
+
+    public PackageBuilder addActivity(ParseComponentArgsCreator argsCreator, ActivityInfo info) {
+        mPkg.activities.add(new PackageParser.Activity(argsCreator.create(mPkg), info));
+        return this;
+    }
+
+    public interface ActivityIntentInfoCreator {
+        PackageParser.ActivityIntentInfo create(PackageParser.Activity activity);
+    }
+
+    public PackageBuilder addActivityIntentInfo(
+            int activityIndex, ActivityIntentInfoCreator creator) {
+        final PackageParser.Activity activity = mPkg.activities.get(activityIndex);
+        activity.intents.add(creator.create(activity));
+        return this;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 13a8eb1..e33d8ca 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
@@ -557,6 +558,9 @@
         pkg.mRequiredForAllUsers = true;
         pkg.visibleToInstantApps = true;
         pkg.use32bitAbi = true;
+        pkg.mForceQueryable = true;
+        pkg.mQueriesPackages = new ArrayList<>(Arrays.asList("foo27"));
+        pkg.mQueriesIntents = new ArrayList<>(Arrays.asList(new Intent("foo28")));
     }
 
     private static void assertAllFieldsExist(PackageParser.Package pkg) throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
new file mode 100644
index 0000000..06c6314
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -0,0 +1,162 @@
+/*
+ * 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.pm;
+
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.util.SparseArray;
+
+import java.io.File;
+import java.util.List;
+
+class PackageSettingBuilder {
+    private String mName;
+    private String mRealName;
+    private String mCodePath;
+    private String mResourcePath;
+    private String mLegacyNativeLibraryPathString;
+    private String mPrimaryCpuAbiString;
+    private String mSecondaryCpuAbiString;
+    private String mCpuAbiOverrideString;
+    private long mPVersionCode;
+    private int mPkgFlags;
+    private int mPrivateFlags;
+    private String mParentPackageName;
+    private List<String> mChildPackageNames;
+    private int mSharedUserId;
+    private String[] mUsesStaticLibraries;
+    private long[] mUsesStaticLibrariesVersions;
+    private String mVolumeUuid;
+    private SparseArray<PackageUserState> mUserStates = new SparseArray<>();
+    private PackageParser.Package mPkg;
+
+    public PackageSettingBuilder setPackage(PackageParser.Package pkg) {
+        this.mPkg = pkg;
+        return this;
+    }
+
+    public PackageSettingBuilder setName(String name) {
+        this.mName = name;
+        return this;
+    }
+
+    public PackageSettingBuilder setRealName(String realName) {
+        this.mRealName = realName;
+        return this;
+    }
+
+    public PackageSettingBuilder setCodePath(String codePath) {
+        this.mCodePath = codePath;
+        return this;
+    }
+
+    public PackageSettingBuilder setResourcePath(String resourcePath) {
+        this.mResourcePath = resourcePath;
+        return this;
+    }
+
+    public PackageSettingBuilder setLegacyNativeLibraryPathString(
+            String legacyNativeLibraryPathString) {
+        this.mLegacyNativeLibraryPathString = legacyNativeLibraryPathString;
+        return this;
+    }
+
+    public PackageSettingBuilder setPrimaryCpuAbiString(String primaryCpuAbiString) {
+        this.mPrimaryCpuAbiString = primaryCpuAbiString;
+        return this;
+    }
+
+    public PackageSettingBuilder setSecondaryCpuAbiString(String secondaryCpuAbiString) {
+        this.mSecondaryCpuAbiString = secondaryCpuAbiString;
+        return this;
+    }
+
+    public PackageSettingBuilder setCpuAbiOverrideString(String cpuAbiOverrideString) {
+        this.mCpuAbiOverrideString = cpuAbiOverrideString;
+        return this;
+    }
+
+    public PackageSettingBuilder setPVersionCode(long pVersionCode) {
+        this.mPVersionCode = pVersionCode;
+        return this;
+    }
+
+    public PackageSettingBuilder setPkgFlags(int pkgFlags) {
+        this.mPkgFlags = pkgFlags;
+        return this;
+    }
+
+    public PackageSettingBuilder setPrivateFlags(int privateFlags) {
+        this.mPrivateFlags = privateFlags;
+        return this;
+    }
+
+    public PackageSettingBuilder setParentPackageName(String parentPackageName) {
+        this.mParentPackageName = parentPackageName;
+        return this;
+    }
+
+    public PackageSettingBuilder setChildPackageNames(List<String> childPackageNames) {
+        this.mChildPackageNames = childPackageNames;
+        return this;
+    }
+
+    public PackageSettingBuilder setSharedUserId(int sharedUserId) {
+        this.mSharedUserId = sharedUserId;
+        return this;
+    }
+
+    public PackageSettingBuilder setUsesStaticLibraries(String[] usesStaticLibraries) {
+        this.mUsesStaticLibraries = usesStaticLibraries;
+        return this;
+    }
+
+    public PackageSettingBuilder setUsesStaticLibrariesVersions(
+            long[] usesStaticLibrariesVersions) {
+        this.mUsesStaticLibrariesVersions = usesStaticLibrariesVersions;
+        return this;
+    }
+
+    public PackageSettingBuilder setVolumeUuid(String volumeUuid) {
+        this.mVolumeUuid = volumeUuid;
+        return this;
+    }
+
+    public PackageSettingBuilder setInstantAppUserState(int userId, boolean isInstant) {
+        if (mUserStates.indexOfKey(userId) < 0) {
+            mUserStates.put(userId, new PackageUserState());
+        }
+        mUserStates.get(userId).instantApp = isInstant;
+        return this;
+    }
+
+    public PackageSetting build() {
+        final PackageSetting packageSetting = new PackageSetting(mName, mRealName,
+                new File(mCodePath), new File(mResourcePath),
+                mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, mSecondaryCpuAbiString,
+                mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mParentPackageName,
+                mChildPackageNames, mSharedUserId, mUsesStaticLibraries,
+                mUsesStaticLibrariesVersions);
+        packageSetting.pkg = mPkg;
+        packageSetting.volumeUuid = this.mVolumeUuid;
+        for (int i = 0; i < mUserStates.size(); i++) {
+            packageSetting.setUserState(mUserStates.keyAt(i), mUserStates.valueAt(i));
+        }
+        return packageSetting;
+
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
new file mode 100644
index 0000000..34a3f86
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.PackageParser;
+import android.os.UserHandle;
+
+class ScanRequestBuilder {
+    private final PackageParser.Package mPkg;
+    private PackageParser.Package mOldPkg;
+    private SharedUserSetting mSharedUserSetting;
+    private PackageSetting mPkgSetting;
+    private PackageSetting mDisabledPkgSetting;
+    private PackageSetting mOriginalPkgSetting;
+    private String mRealPkgName;
+    private int mParseFlags;
+    private int mScanFlags;
+    private UserHandle mUser;
+    private boolean mIsPlatformPackage;
+
+    ScanRequestBuilder(PackageParser.Package pkg) {
+        this.mPkg = pkg;
+    }
+
+    public ScanRequestBuilder setOldPkg(PackageParser.Package oldPkg) {
+        this.mOldPkg = oldPkg;
+        return this;
+    }
+
+    public ScanRequestBuilder setSharedUserSetting(SharedUserSetting sharedUserSetting) {
+        this.mSharedUserSetting = sharedUserSetting;
+        return this;
+    }
+
+    public ScanRequestBuilder setPkgSetting(PackageSetting pkgSetting) {
+        this.mPkgSetting = pkgSetting;
+        return this;
+    }
+
+    public ScanRequestBuilder setDisabledPkgSetting(PackageSetting disabledPkgSetting) {
+        this.mDisabledPkgSetting = disabledPkgSetting;
+        return this;
+    }
+
+    public ScanRequestBuilder setOriginalPkgSetting(PackageSetting originalPkgSetting) {
+        this.mOriginalPkgSetting = originalPkgSetting;
+        return this;
+    }
+
+    public ScanRequestBuilder setRealPkgName(String realPkgName) {
+        this.mRealPkgName = realPkgName;
+        return this;
+    }
+
+    public ScanRequestBuilder setParseFlags(int parseFlags) {
+        this.mParseFlags = parseFlags;
+        return this;
+    }
+
+    public ScanRequestBuilder addParseFlag(int parseFlag) {
+        this.mParseFlags |= parseFlag;
+        return this;
+    }
+
+    public ScanRequestBuilder setScanFlags(int scanFlags) {
+        this.mScanFlags = scanFlags;
+        return this;
+    }
+
+    public ScanRequestBuilder addScanFlag(int scanFlag) {
+        this.mScanFlags |= scanFlag;
+        return this;
+    }
+
+    public ScanRequestBuilder setUser(UserHandle user) {
+        this.mUser = user;
+        return this;
+    }
+
+    public ScanRequestBuilder setIsPlatformPackage(boolean isPlatformPackage) {
+        this.mIsPlatformPackage = isPlatformPackage;
+        return this;
+    }
+
+    PackageManagerService.ScanRequest build() {
+        return new PackageManagerService.ScanRequest(
+                mPkg, mSharedUserSetting, mOldPkg, mPkgSetting, mDisabledPkgSetting,
+                mOriginalPkgSetting, mRealPkgName, mParseFlags, mScanFlags, mIsPlatformPackage,
+                mUser);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
new file mode 100644
index 0000000..cc70ef8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -0,0 +1,520 @@
+/*
+ * 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.pm;
+
+import static android.content.pm.SharedLibraryInfo.TYPE_DYNAMIC;
+import static android.content.pm.SharedLibraryInfo.TYPE_STATIC;
+import static android.content.pm.SharedLibraryInfo.VERSION_UNDEFINED;
+
+import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
+import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.collection.IsArrayContainingInOrder.arrayContaining;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertNotSame;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.SharedLibraryInfo;
+import android.os.Environment;
+import android.os.UserHandle;
+import android.os.UserManagerInternal;
+import android.platform.test.annotations.Presubmit;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.File;
+
+@RunWith(MockitoJUnitRunner.class)
+@Presubmit
+public class ScanTests {
+
+    private static final String DUMMY_PACKAGE_NAME = "some.app.to.test";
+
+    @Mock
+    PackageAbiHelper mMockPackageAbiHelper;
+    @Mock
+    UserManagerInternal mMockUserManager;
+
+    @Before
+    public void setupDefaultUser() {
+        when(mMockUserManager.getUserIds()).thenReturn(new int[]{0});
+    }
+
+    @Test
+    public void newInstallSimpleAllNominal() throws Exception {
+        final PackageManagerService.ScanRequest scanRequest =
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                        .addScanFlag(PackageManagerService.SCAN_NEW_INSTALL)
+                        .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP)
+                        .build();
+
+
+        final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+        assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/);
+        assertThat(scanResult.existingSettingCopied, is(false));
+        verify(mMockPackageAbiHelper, never()).derivePackageAbi(any(PackageParser.Package.class),
+                anyString() /*abioverride*/, anyBoolean() /*extractNativeLibs*/);
+        verify(mMockPackageAbiHelper).setNativeLibraryPaths(
+                scanResult.pkgSetting.pkg, PackageManagerService.sAppLib32InstallDir);
+    }
+
+    @Test
+    public void newInstallForAllUsers() throws Exception {
+        final int[] userIds = {0, 10, 11};
+        when(mMockUserManager.getUserIds()).thenReturn(userIds);
+
+        final PackageManagerService.ScanRequest scanRequest =
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                        .setRealPkgName(null)
+                        .addScanFlag(PackageManagerService.SCAN_NEW_INSTALL)
+                        .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP)
+                        .build();
+        final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+        for (int uid : userIds) {
+            assertThat(scanResult.pkgSetting.readUserState(uid).installed, is(true));
+        }
+    }
+
+    @Test
+    public void installRealPackageName() throws Exception {
+        final PackageManagerService.ScanRequest scanRequest =
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                        .setRealPkgName("com.package.real")
+                        .build();
+
+        final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+        assertThat(scanResult.pkgSetting.realName, is("com.package.real"));
+
+        final PackageManagerService.ScanRequest scanRequestNoRealPkg =
+                createBasicScanRequestBuilder(
+                        createBasicPackage(DUMMY_PACKAGE_NAME)
+                                .setRealPackageName("com.package.real").build())
+                        .build();
+
+        final PackageManagerService.ScanResult scanResultNoReal = executeScan(scanRequestNoRealPkg);
+        assertThat(scanResultNoReal.pkgSetting.realName, nullValue());
+    }
+
+    @Test
+    public void updateSimpleNominal() throws Exception {
+        when(mMockUserManager.getUserIds()).thenReturn(new int[]{0});
+
+        final PackageSetting pkgSetting = createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME)
+                .setPrimaryCpuAbiString("primaryCpuAbi")
+                .setSecondaryCpuAbiString("secondaryCpuAbi")
+                .build();
+        final PackageManagerService.ScanRequest scanRequest =
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                        .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP)
+                        .setPkgSetting(pkgSetting)
+                        .build();
+
+
+        final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+        assertThat(scanResult.existingSettingCopied, is(true));
+
+        // ensure we don't overwrite the existing pkgSetting, in case something post-scan fails
+        assertNotSame(pkgSetting, scanResult.pkgSetting);
+
+        assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/);
+
+        assertThat(scanResult.pkgSetting.primaryCpuAbiString, is("primaryCpuAbi"));
+        assertThat(scanResult.pkgSetting.secondaryCpuAbiString, is("secondaryCpuAbi"));
+        assertThat(scanResult.pkgSetting.cpuAbiOverrideString, nullValue());
+
+        verify(mMockPackageAbiHelper, never()).derivePackageAbi(any(PackageParser.Package.class),
+                anyString() /*abioverride*/, anyBoolean() /*extractNativeLibs*/);
+        verify(mMockPackageAbiHelper).setNativeLibraryPaths(
+                scanResult.pkgSetting.pkg, PackageManagerService.sAppLib32InstallDir);
+    }
+
+    @Test
+    public void updateInstantSimpleNominal() throws Exception {
+        when(mMockUserManager.getUserIds()).thenReturn(new int[]{0});
+
+        final PackageSetting existingPkgSetting =
+                createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME)
+                        .setInstantAppUserState(0, true)
+                        .build();
+
+        final PackageManagerService.ScanRequest scanRequest =
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                        .setPkgSetting(existingPkgSetting)
+                        .build();
+
+
+        final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+        assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, true /*isInstant*/);
+    }
+
+    @Test
+    public void installStaticSharedLibrary() throws Exception {
+        final PackageParser.Package pkg = createBasicPackage("static.lib.pkg.123")
+                .setStaticSharedLib("static.lib", 123L)
+                .setManifestPackageName("static.lib.pkg")
+                .setVersionCodeMajor(1)
+                .setVersionCode(234)
+                .setBaseCodePath("/some/path.apk")
+                .addSplitCodePath("/some/other/path.apk")
+                .build();
+
+        final PackageManagerService.ScanRequest scanRequest = new ScanRequestBuilder(
+                pkg).setUser(UserHandle.of(0)).build();
+
+
+        final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+        assertThat(scanResult.staticSharedLibraryInfo.getPackageName(), is("static.lib.pkg.123"));
+        assertThat(scanResult.staticSharedLibraryInfo.getName(), is("static.lib"));
+        assertThat(scanResult.staticSharedLibraryInfo.getLongVersion(), is(123L));
+        assertThat(scanResult.staticSharedLibraryInfo.getType(), is(TYPE_STATIC));
+        assertThat(scanResult.staticSharedLibraryInfo.getDeclaringPackage().getPackageName(),
+                is("static.lib.pkg"));
+        assertThat(scanResult.staticSharedLibraryInfo.getDeclaringPackage().getLongVersionCode(),
+                is(pkg.getLongVersionCode()));
+        assertThat(scanResult.staticSharedLibraryInfo.getAllCodePaths(),
+                hasItems("/some/path.apk", "/some/other/path.apk"));
+        assertThat(scanResult.staticSharedLibraryInfo.getDependencies(), nullValue());
+        assertThat(scanResult.staticSharedLibraryInfo.getDependentPackages(), empty());
+    }
+
+    @Test
+    public void installDynamicLibraries() throws Exception {
+        final PackageParser.Package pkg = createBasicPackage("dynamic.lib.pkg")
+                .setManifestPackageName("dynamic.lib.pkg")
+                .addLibraryName("liba")
+                .addLibraryName("libb")
+                .setVersionCodeMajor(1)
+                .setVersionCode(234)
+                .setBaseCodePath("/some/path.apk")
+                .addSplitCodePath("/some/other/path.apk")
+                .build();
+
+        final PackageManagerService.ScanRequest scanRequest =
+                new ScanRequestBuilder(pkg).setUser(UserHandle.of(0)).build();
+
+
+        final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+        final SharedLibraryInfo dynamicLib0 = scanResult.dynamicSharedLibraryInfos.get(0);
+        assertThat(dynamicLib0.getPackageName(), is("dynamic.lib.pkg"));
+        assertThat(dynamicLib0.getName(), is("liba"));
+        assertThat(dynamicLib0.getLongVersion(), is((long) VERSION_UNDEFINED));
+        assertThat(dynamicLib0.getType(), is(TYPE_DYNAMIC));
+        assertThat(dynamicLib0.getDeclaringPackage().getPackageName(), is("dynamic.lib.pkg"));
+        assertThat(dynamicLib0.getDeclaringPackage().getLongVersionCode(),
+                is(pkg.getLongVersionCode()));
+        assertThat(dynamicLib0.getAllCodePaths(),
+                hasItems("/some/path.apk", "/some/other/path.apk"));
+        assertThat(dynamicLib0.getDependencies(), nullValue());
+        assertThat(dynamicLib0.getDependentPackages(), empty());
+
+        final SharedLibraryInfo dynamicLib1 = scanResult.dynamicSharedLibraryInfos.get(1);
+        assertThat(dynamicLib1.getPackageName(), is("dynamic.lib.pkg"));
+        assertThat(dynamicLib1.getName(), is("libb"));
+        assertThat(dynamicLib1.getLongVersion(), is((long) VERSION_UNDEFINED));
+        assertThat(dynamicLib1.getType(), is(TYPE_DYNAMIC));
+        assertThat(dynamicLib1.getDeclaringPackage().getPackageName(), is("dynamic.lib.pkg"));
+        assertThat(dynamicLib1.getDeclaringPackage().getLongVersionCode(),
+                is(pkg.getLongVersionCode()));
+        assertThat(dynamicLib1.getAllCodePaths(),
+                hasItems("/some/path.apk", "/some/other/path.apk"));
+        assertThat(dynamicLib1.getDependencies(), nullValue());
+        assertThat(dynamicLib1.getDependentPackages(), empty());
+    }
+
+    @Test
+    public void volumeUuidChangesOnUpdate() throws Exception {
+        final PackageSetting pkgSetting =
+                createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME)
+                        .setVolumeUuid("someUuid")
+                        .build();
+
+        final PackageParser.Package basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
+                .setApplicationInfoVolumeUuid("someNewUuid")
+                .build();
+
+
+        final PackageManagerService.ScanResult scanResult = executeScan(
+                new ScanRequestBuilder(basicPackage).setPkgSetting(pkgSetting).build());
+
+        assertThat(scanResult.pkgSetting.volumeUuid, is("someNewUuid"));
+    }
+
+    @Test
+    public void scanFirstBoot_derivesAbis() throws Exception {
+        final PackageSetting pkgSetting =
+                createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME).build();
+
+        final PackageParser.Package basicPackage =
+                createBasicPackage(DUMMY_PACKAGE_NAME)
+                        .setCpuAbiOVerride("testOverride")
+                        .build();
+
+
+        executeScan(new ScanRequestBuilder(basicPackage)
+                .setPkgSetting(pkgSetting)
+                .addScanFlag(SCAN_FIRST_BOOT_OR_UPGRADE)
+                .build());
+
+        verify(mMockPackageAbiHelper).derivePackageAbi(basicPackage, "testOverride", true);
+    }
+
+    @Test
+    public void scanWithOriginalPkgSetting_packageNameChanges() throws Exception {
+        final PackageSetting originalPkgSetting =
+                createBasicPackageSettingBuilder("original.package").build();
+
+        final PackageParser.Package basicPackage =
+                createBasicPackage(DUMMY_PACKAGE_NAME)
+                        .build();
+
+
+        final PackageManagerService.ScanResult result =
+                executeScan(new ScanRequestBuilder(basicPackage)
+                        .setOriginalPkgSetting(originalPkgSetting)
+                        .build());
+
+        assertThat(result.request.pkg.packageName, is("original.package"));
+    }
+
+    @Test
+    public void updateInstant_changeToFull() throws Exception {
+        when(mMockUserManager.getUserIds()).thenReturn(new int[]{0});
+
+        final PackageSetting existingPkgSetting =
+                createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME)
+                        .setInstantAppUserState(0, true)
+                        .build();
+
+        final PackageManagerService.ScanRequest scanRequest =
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                        .setPkgSetting(existingPkgSetting)
+                        .addScanFlag(SCAN_AS_FULL_APP)
+                        .build();
+
+
+        final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+        assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/);
+    }
+
+    @Test
+    public void updateFull_changeToInstant() throws Exception {
+        when(mMockUserManager.getUserIds()).thenReturn(new int[]{0});
+
+        final PackageSetting existingPkgSetting =
+                createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME)
+                        .setInstantAppUserState(0, false)
+                        .build();
+
+        final PackageManagerService.ScanRequest scanRequest =
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                        .setPkgSetting(existingPkgSetting)
+                        .addScanFlag(SCAN_AS_INSTANT_APP)
+                        .build();
+
+
+        final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+        assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, true /*isInstant*/);
+    }
+
+    @Test
+    public void updateSystemApp_applicationInfoFlagSet() throws Exception {
+        final PackageSetting existingPkgSetting =
+                createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME)
+                        .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
+                        .build();
+
+        final PackageManagerService.ScanRequest scanRequest =
+                createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build())
+                        .setPkgSetting(existingPkgSetting)
+                        .setDisabledPkgSetting(existingPkgSetting)
+                        .addScanFlag(SCAN_NEW_INSTALL)
+                        .build();
+
+        final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+        assertThat(scanResult.request.pkg.applicationInfo.flags,
+                hasFlag(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
+    }
+
+    @Test
+    public void factoryTestFlagSet() throws Exception {
+        final PackageParser.Package basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
+                .addPermissionRequest(Manifest.permission.FACTORY_TEST)
+                .build();
+
+        final PackageManagerService.ScanResult scanResult = PackageManagerService.scanPackageOnlyLI(
+                createBasicScanRequestBuilder(basicPackage).build(),
+                new PackageManagerService.Injector(mMockUserManager, mMockPackageAbiHelper),
+                true /*isUnderFactoryTest*/,
+                System.currentTimeMillis());
+
+        assertThat(scanResult.request.pkg.applicationInfo.flags,
+                hasFlag(ApplicationInfo.FLAG_FACTORY_TEST));
+    }
+
+    @Test
+    public void scanSystemApp_isOrphanedTrue() throws Exception {
+        final PackageParser.Package pkg = createBasicPackage(DUMMY_PACKAGE_NAME)
+                .addApplicationInfoFlag(ApplicationInfo.FLAG_SYSTEM)
+                .build();
+
+        final PackageManagerService.ScanRequest scanRequest =
+                createBasicScanRequestBuilder(pkg)
+                        .build();
+
+        final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+
+        assertThat(scanResult.pkgSetting.isOrphaned, is(true));
+    }
+
+    private static Matcher<Integer> hasFlag(final int flag) {
+        return new BaseMatcher<Integer>() {
+            @Override public void describeTo(Description description) {
+                description.appendText("flags ");
+            }
+
+            @Override public boolean matches(Object item) {
+                return ((int) item & flag) != 0;
+            }
+
+            @Override
+            public void describeMismatch(Object item, Description mismatchDescription) {
+                mismatchDescription
+                        .appendValue(item)
+                        .appendText(" does not contain flag ")
+                        .appendValue(flag);
+            }
+        };
+    }
+
+    private PackageManagerService.ScanResult executeScan(
+            PackageManagerService.ScanRequest scanRequest) throws PackageManagerException {
+        return PackageManagerService.scanPackageOnlyLI(
+                scanRequest,
+                new PackageManagerService.Injector(mMockUserManager, mMockPackageAbiHelper),
+                false /*isUnderFactoryTest*/,
+                System.currentTimeMillis());
+    }
+
+    private static String createResourcePath(String packageName) {
+        return "/data/app/" + packageName + "-randompath/base.apk";
+    }
+
+    private static String createCodePath(String packageName) {
+        return "/data/app/" + packageName + "-randompath";
+    }
+
+    private static PackageSettingBuilder createBasicPackageSettingBuilder(String packageName) {
+        return new PackageSettingBuilder()
+                .setName(packageName)
+                .setCodePath(createCodePath(packageName))
+                .setResourcePath(createResourcePath(packageName));
+    }
+
+    private static ScanRequestBuilder createBasicScanRequestBuilder(PackageParser.Package pkg) {
+        return new ScanRequestBuilder(pkg)
+                .setUser(UserHandle.of(0));
+    }
+
+
+    private static PackageBuilder createBasicPackage(String packageName) {
+        return new PackageBuilder(packageName)
+                .setCodePath("/data/tmp/randompath")
+                .setApplicationInfoCodePath(createCodePath(packageName))
+                .setApplicationInfoResourcePath(createResourcePath(packageName))
+                .setApplicationInfoVolumeUuid("volumeUuid")
+                .setBaseCodePath("/data/tmp/randompath/base.apk")
+                .addUsesStaticLibrary("some.static.library", 234L)
+                .addUsesStaticLibrary("some.other.static.library", 456L)
+                .setApplicationInfoNativeLibraryRootDir("/data/tmp/randompath/base.apk:/lib")
+                .setVersionCodeMajor(1)
+                .setVersionCode(2345);
+    }
+
+    private static void assertBasicPackageScanResult(
+            PackageManagerService.ScanResult scanResult, String packageName, boolean isInstant) {
+        assertThat(scanResult.success, is(true));
+
+        final PackageSetting pkgSetting = scanResult.pkgSetting;
+        assertBasicPackageSetting(scanResult, packageName, isInstant, pkgSetting);
+
+        final ApplicationInfo applicationInfo = pkgSetting.pkg.applicationInfo;
+        verifyBasicApplicationInfo(scanResult, applicationInfo);
+
+    }
+
+    private static void assertBasicPackageSetting(PackageManagerService.ScanResult scanResult,
+            String packageName, boolean isInstant, PackageSetting pkgSetting) {
+        assertThat(pkgSetting.pkg.packageName, is(packageName));
+        assertThat(pkgSetting.getInstantApp(0), is(isInstant));
+        assertThat(pkgSetting.usesStaticLibraries,
+                arrayContaining("some.static.library", "some.other.static.library"));
+        assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L}));
+        assertThat(pkgSetting.pkg, is(scanResult.request.pkg));
+        assertThat(pkgSetting.pkg.mExtras, is(pkgSetting));
+        assertThat(pkgSetting.legacyNativeLibraryPathString,
+                is("/data/tmp/randompath/base.apk:/lib"));
+        assertThat(pkgSetting.codePath, is(new File(createCodePath(packageName))));
+        assertThat(pkgSetting.resourcePath, is(new File(createResourcePath(packageName))));
+        assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345)));
+    }
+
+    private static void verifyBasicApplicationInfo(PackageManagerService.ScanResult scanResult,
+            ApplicationInfo applicationInfo) {
+        assertThat(applicationInfo.processName, is(scanResult.request.pkg.packageName));
+
+        final int uid = applicationInfo.uid;
+        assertThat(UserHandle.getUserId(uid), is(UserHandle.USER_SYSTEM));
+
+        final String calculatedCredentialId = Environment.getDataUserCePackageDirectory(
+                applicationInfo.volumeUuid, UserHandle.USER_SYSTEM,
+                scanResult.request.pkg.packageName).getAbsolutePath();
+        assertThat(applicationInfo.credentialProtectedDataDir, is(calculatedCredentialId));
+        assertThat(applicationInfo.dataDir, is(applicationInfo.credentialProtectedDataDir));
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index f5002ac..3b336eb 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -7,10 +7,12 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -159,4 +161,25 @@
         verify(mSliceService).removePinnedSlice(eq(TEST_URI));
         assertFalse(mPinnedSliceManager.hasPinOrListener());
     }
+
+    @Test
+    public void testPinFailed() throws Exception {
+        // Throw exception when trying to pin
+        doAnswer(invocation -> {
+            throw new Exception("Pin failed");
+        }).when(mIContentProvider).call(
+                anyString(), anyString(), anyString(), eq(null), any());
+
+        TestableLooper.get(this).processAllMessages();
+
+        // When pinned for the first time, a pinned message should be sent.
+        mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken);
+        TestableLooper.get(this).processAllMessages();
+
+        verify(mIContentProvider).call(anyString(), anyString(), eq(SliceProvider.METHOD_PIN),
+                eq(null), argThat(b -> {
+                    assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI));
+                    return true;
+                }));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index a08923b..3d94467 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -842,4 +842,51 @@
         // Ensure the activity is moved to secondary display.
         assertEquals(secondaryDisplay, topActivity.getDisplay());
     }
+
+    /**
+     * This test ensures that starting an activity with the freeze-task-list activity option will
+     * actually freeze the task list
+     */
+    @Test
+    public void testFreezeTaskListActivityOption() {
+        RecentTasks recentTasks = mock(RecentTasks.class);
+        mService.mStackSupervisor.setRecentTasks(recentTasks);
+        doReturn(true).when(recentTasks).isCallerRecents(anyInt());
+
+        final ActivityStarter starter = prepareStarter(0 /* flags */);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setFreezeRecentTasksReordering();
+
+        starter.setReason("testFreezeTaskListActivityOption")
+                .setActivityOptions(new SafeActivityOptions(options))
+                .execute();
+
+        verify(recentTasks, times(1)).setFreezeTaskListReordering();
+        verify(recentTasks, times(0)).resetFreezeTaskListReorderingOnTimeout();
+    }
+
+    /**
+     * This test ensures that if we froze the task list as a part of starting an activity that fails
+     * to start, that we also reset the task list.
+     */
+    @Test
+    public void testFreezeTaskListActivityOptionFailedStart_expectResetFreezeTaskList() {
+        RecentTasks recentTasks = mock(RecentTasks.class);
+        mService.mStackSupervisor.setRecentTasks(recentTasks);
+        doReturn(true).when(recentTasks).isCallerRecents(anyInt());
+
+        final ActivityStarter starter = prepareStarter(0 /* flags */);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setFreezeRecentTasksReordering();
+
+        starter.setReason("testFreezeTaskListActivityOptionFailedStart")
+                .setActivityOptions(new SafeActivityOptions(options))
+                .execute();
+
+        // Simulate a failed start
+        starter.postStartActivityProcessing(null, START_ABORTED, null);
+
+        verify(recentTasks, times(1)).setFreezeTaskListReordering();
+        verify(recentTasks, times(1)).resetFreezeTaskListReorderingOnTimeout();
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 26cd63c..cd292b2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -25,6 +25,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
@@ -35,10 +36,12 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 
 import android.os.Binder;
@@ -79,6 +82,7 @@
             // Hold the lock to protect the stubbing from being accessed by other threads.
             spyOn(mWm.mRoot);
             doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
+            doReturn(mDisplayContent).when(mWm.mRoot).getDisplayContent(anyInt());
         }
         when(mMockRunner.asBinder()).thenReturn(new Binder());
         mController = new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks,
@@ -135,7 +139,7 @@
         hiddenAppWindow.setHidden(true);
         mDisplayContent.getConfiguration().windowConfiguration.setRotation(
                 mDisplayContent.getRotation());
-        mController.initialize(mDisplayContent, ACTIVITY_TYPE_HOME, new SparseBooleanArray());
+        mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray());
 
         // Ensure that we are animating the target activity as well
         assertTrue(mController.isAnimatingTask(homeAppWindow.getTask()));
@@ -144,7 +148,7 @@
     }
 
     @Test
-    public void testCancelAnimationWithScreenShot() throws Exception {
+    public void testDeferCancelAnimation() throws Exception {
         mWm.setRecentsAnimationController(mController);
         final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -156,8 +160,31 @@
         mController.addAnimation(appWindow.getTask(), false /* isRecentTaskInvisible */);
         assertTrue(mController.isAnimatingTask(appWindow.getTask()));
 
-        mController.setCancelWithDeferredScreenshotLocked(true);
-        mController.cancelAnimationWithScreenShot();
+        mController.setDeferredCancel(true /* deferred */, false /* screenshot */);
+        mController.cancelAnimationWithScreenshot(false /* screenshot */);
+        verify(mMockRunner).onAnimationCanceled(false /* deferredWithScreenshot */);
+        assertNull(mController.mRecentScreenshotAnimator);
+
+        // Simulate the app transition finishing
+        mController.mAppTransitionListener.onAppTransitionStartingLocked(0, 0, 0, 0);
+        verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false);
+    }
+
+    @Test
+    public void testDeferCancelAnimationWithScreenShot() throws Exception {
+        mWm.setRecentsAnimationController(mController);
+        final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
+        appWindow.addWindow(win1);
+        assertEquals(appWindow.getTask().getTopVisibleAppToken(), appWindow);
+        assertEquals(appWindow.findMainWindow(), win1);
+
+        mController.addAnimation(appWindow.getTask(), false /* isRecentTaskInvisible */);
+        assertTrue(mController.isAnimatingTask(appWindow.getTask()));
+
+        mController.setDeferredCancel(true /* deferred */, true /* screenshot */);
+        mController.cancelAnimationWithScreenshot(true /* screenshot */);
         verify(mMockRunner).onAnimationCanceled(true /* deferredWithScreenshot */);
         assertNotNull(mController.mRecentScreenshotAnimator);
         assertTrue(mController.mRecentScreenshotAnimator.isAnimating());
@@ -185,7 +212,7 @@
 
         // Assume appWindow transition should animate when no
         // IRecentsAnimationController#setCancelWithDeferredScreenshot called.
-        assertFalse(mController.shouldCancelWithDeferredScreenshot());
+        assertFalse(mController.shouldDeferCancelWithScreenshot());
         assertTrue(appWindow.shouldAnimate(TRANSIT_ACTIVITY_CLOSE));
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 9630b7d..0e119e3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -274,12 +274,13 @@
 
         // Assume recents animation already started, set a state that cancel recents animation
         // with screenshot.
-        doReturn(true).when(mRecentsAnimationController).shouldCancelWithDeferredScreenshot();
+        doReturn(true).when(mRecentsAnimationController).shouldDeferCancelUntilNextTransition();
+        doReturn(true).when(mRecentsAnimationController).shouldDeferCancelWithScreenshot();
         // Start another fullscreen activity.
         fullscreenStack2.moveToFront("Activity start");
 
-        // Ensure that the recents animation was canceled by cancelOnNextTransitionStart().
-        verify(mRecentsAnimationController, times(1)).cancelOnNextTransitionStart();
+        // Ensure that the recents animation was canceled by setCancelOnNextTransitionStart().
+        verify(mRecentsAnimationController, times(1)).setCancelOnNextTransitionStart();
     }
 
     @Test
@@ -315,7 +316,7 @@
         // Ensure that the recents animation was NOT canceled
         verify(mService.mWindowManager, times(0)).cancelRecentsAnimationSynchronously(
                 eq(REORDER_KEEP_IN_PLACE), any());
-        verify(mRecentsAnimationController, times(0)).cancelOnNextTransitionStart();
+        verify(mRecentsAnimationController, times(0)).setCancelOnNextTransitionStart();
     }
 
     @Test
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 29398b6..2cebebf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -1251,6 +1251,22 @@
         assertEquals(startingBounds, adjustedBounds);
     }
 
+    @Test
+    public void testNoMultiDisplaySupports() {
+        final boolean orgValue = mService.mSupportsMultiDisplay;
+        final TestActivityDisplay display = createNewActivityDisplay(WINDOWING_MODE_FULLSCREEN);
+        mCurrent.mPreferredDisplayId = display.mDisplayId;
+
+        try {
+            mService.mSupportsMultiDisplay = false;
+            assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                    mActivity, /* source */ null, /* options */ null, mCurrent, mResult));
+            assertEquals(DEFAULT_DISPLAY, mResult.mPreferredDisplayId);
+        } finally {
+            mService.mSupportsMultiDisplay = orgValue;
+        }
+    }
+
     private TestActivityDisplay createNewActivityDisplay(int windowingMode) {
         final TestActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
         display.setWindowingMode(windowingMode);
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index 7a1678a..a4906d7 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -23,4 +23,5 @@
         "src/FrameLayoutInflationActivity.java",
         "src/TextViewInflationActivity.java",
     ],
+    platform_apis: true,
 }
diff --git a/startop/scripts/iorap/compiler.py b/startop/scripts/iorap/compiler.py
index a335b30..9527e28 100755
--- a/startop/scripts/iorap/compiler.py
+++ b/startop/scripts/iorap/compiler.py
@@ -305,7 +305,7 @@
     transform_perfetto_trace_to_systrace(options.perfetto_trace_file,
                                          trace_file.name)
     return run(sql_db_path,
-               options.trace_file,
+               trace_file.name,
                options.trace_duration,
                options.output_file,
                inode_table,
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index 6047e8c..09f9c04 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -102,6 +102,12 @@
     case Instruction::Op::kCheckCast:
       out << "kCheckCast";
       return out;
+    case Instruction::Op::kGetStaticField:
+      out << "kGetStaticField";
+      return out;
+    case Instruction::Op::kSetStaticField:
+      out << "kSetStaticField";
+      return out;
   }
 }
 
@@ -229,6 +235,22 @@
   return type;
 }
 
+ir::FieldDecl* DexBuilder::GetOrAddField(TypeDescriptor parent, const std::string& name,
+                                         TypeDescriptor type) {
+  const auto key = std::make_tuple(parent, name);
+  if (field_decls_by_key_.find(key) != field_decls_by_key_.end()) {
+    return field_decls_by_key_[key];
+  }
+
+  ir::FieldDecl* field = Alloc<ir::FieldDecl>();
+  field->parent = GetOrAddType(parent);
+  field->name = GetOrAddString(name);
+  field->type = GetOrAddType(type);
+  dex_file_->fields_map[field->orig_index] = field;
+  field_decls_by_key_[key] = field;
+  return field;
+}
+
 ir::Proto* Prototype::Encode(DexBuilder* dex) const {
   auto* proto = dex->Alloc<ir::Proto>();
   proto->shorty = dex->GetOrAddString(Shorty());
@@ -360,6 +382,9 @@
       return EncodeNew(instruction);
     case Instruction::Op::kCheckCast:
       return EncodeCast(instruction);
+    case Instruction::Op::kGetStaticField:
+    case Instruction::Op::kSetStaticField:
+      return EncodeStaticFieldOp(instruction);
   }
 }
 
@@ -428,7 +453,7 @@
     // first move all the arguments into contiguous temporary registers.
     std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>();
 
-    const auto& prototype = dex_->GetPrototypeByMethodId(instruction.method_id());
+    const auto& prototype = dex_->GetPrototypeByMethodId(instruction.index_argument());
     CHECK(prototype.has_value());
 
     for (size_t i = 0; i < instruction.args().size(); ++i) {
@@ -452,12 +477,12 @@
 
     Encode3rc(InvokeToInvokeRange(opcode),
               instruction.args().size(),
-              instruction.method_id(),
+              instruction.index_argument(),
               RegisterValue(scratch[0]));
   } else {
     Encode35c(opcode,
               instruction.args().size(),
-              instruction.method_id(),
+              instruction.index_argument(),
               arguments[0],
               arguments[1],
               arguments[2],
@@ -514,6 +539,35 @@
   Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value());
 }
 
+void MethodBuilder::EncodeStaticFieldOp(const Instruction& instruction) {
+  switch (instruction.opcode()) {
+    case Instruction::Op::kGetStaticField: {
+      CHECK(instruction.dest().has_value());
+      CHECK(instruction.dest()->is_variable());
+      CHECK_EQ(0, instruction.args().size());
+
+      Encode21c(::art::Instruction::SGET,
+                RegisterValue(*instruction.dest()),
+                instruction.index_argument());
+      break;
+    }
+    case Instruction::Op::kSetStaticField: {
+      CHECK(!instruction.dest().has_value());
+      const auto& args = instruction.args();
+      CHECK_EQ(1, args.size());
+      CHECK(args[0].is_variable());
+
+      Encode21c(::art::Instruction::SPUT,
+                RegisterValue(args[0]),
+                instruction.index_argument());
+      break;
+    }
+    default: {
+      LOG(FATAL) << "Unsupported static field operation";
+    }
+  }
+}
+
 size_t MethodBuilder::RegisterValue(const Value& value) const {
   if (value.is_register()) {
     return value.value();
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 541d800..3f9ac43 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -153,6 +153,7 @@
     kBranchEqz,
     kBranchNEqz,
     kCheckCast,
+    kGetStaticField,
     kInvokeDirect,
     kInvokeInterface,
     kInvokeStatic,
@@ -162,6 +163,7 @@
     kNew,
     kReturn,
     kReturnObject,
+    kSetStaticField
   };
 
   ////////////////////////
@@ -170,12 +172,12 @@
 
   // For instructions with no return value and no arguments.
   static inline Instruction OpNoArgs(Op opcode) {
-    return Instruction{opcode, /*method_id*/ 0, /*dest*/ {}};
+    return Instruction{opcode, /*index_argument*/ 0, /*dest*/ {}};
   }
   // For most instructions, which take some number of arguments and have an optional return value.
   template <typename... T>
   static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) {
-    return Instruction{opcode, /*method_id=*/0, /*result_is_object=*/false, dest, args...};
+    return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...};
   }
 
   // A cast instruction. Basically, `(type)val`
@@ -186,77 +188,87 @@
 
   // For method calls.
   template <typename... T>
-  static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest,
+  static inline Instruction InvokeVirtual(size_t index_argument, std::optional<const Value> dest,
                                           Value this_arg, T... args) {
     return Instruction{
-        Op::kInvokeVirtual, method_id, /*result_is_object=*/false, dest, this_arg, args...};
+        Op::kInvokeVirtual, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
   }
   // Returns an object
   template <typename... T>
-  static inline Instruction InvokeVirtualObject(size_t method_id, std::optional<const Value> dest,
+  static inline Instruction InvokeVirtualObject(size_t index_argument, std::optional<const Value> dest,
                                                 Value this_arg, T... args) {
     return Instruction{
-        Op::kInvokeVirtual, method_id, /*result_is_object=*/true, dest, this_arg, args...};
+        Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
   }
   // For direct calls (basically, constructors).
   template <typename... T>
-  static inline Instruction InvokeDirect(size_t method_id, std::optional<const Value> dest,
+  static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest,
                                          Value this_arg, T... args) {
     return Instruction{
-        Op::kInvokeDirect, method_id, /*result_is_object=*/false, dest, this_arg, args...};
+        Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...};
   }
   // Returns an object
   template <typename... T>
-  static inline Instruction InvokeDirectObject(size_t method_id, std::optional<const Value> dest,
+  static inline Instruction InvokeDirectObject(size_t index_argument, std::optional<const Value> dest,
                                                Value this_arg, T... args) {
     return Instruction{
-        Op::kInvokeDirect, method_id, /*result_is_object=*/true, dest, this_arg, args...};
+        Op::kInvokeDirect, index_argument, /*result_is_object=*/true, dest, this_arg, args...};
   }
   // For static calls.
   template <typename... T>
-  static inline Instruction InvokeStatic(size_t method_id, std::optional<const Value> dest,
+  static inline Instruction InvokeStatic(size_t index_argument, std::optional<const Value> dest,
                                          T... args) {
-    return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/false, dest, args...};
+    return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/false, dest, args...};
   }
   // Returns an object
   template <typename... T>
-  static inline Instruction InvokeStaticObject(size_t method_id, std::optional<const Value> dest,
+  static inline Instruction InvokeStaticObject(size_t index_argument, std::optional<const Value> dest,
                                                T... args) {
-    return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/true, dest, args...};
+    return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/true, dest, args...};
   }
   // For static calls.
   template <typename... T>
-  static inline Instruction InvokeInterface(size_t method_id, std::optional<const Value> dest,
+  static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest,
                                             T... args) {
-    return Instruction{Op::kInvokeInterface, method_id, /*result_is_object=*/false, dest, args...};
+    return Instruction{Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...};
+
   }
 
+  static inline Instruction GetStaticField(size_t field_id, Value dest) {
+    return Instruction{Op::kGetStaticField, field_id, dest};
+  }
+
+  static inline Instruction SetStaticField(size_t field_id, Value value) {
+    return Instruction{Op::kSetStaticField, field_id, /*result_is_object=*/false, /*dest=*/{}, value};
+  }
+
+
   ///////////////
   // Accessors //
   ///////////////
 
   Op opcode() const { return opcode_; }
-  size_t method_id() const { return method_id_; }
+  size_t index_argument() const { return index_argument_; }
   bool result_is_object() const { return result_is_object_; }
   const std::optional<const Value>& dest() const { return dest_; }
   const std::vector<const Value>& args() const { return args_; }
 
  private:
-  inline Instruction(Op opcode, size_t method_id, std::optional<const Value> dest)
-      : opcode_{opcode}, method_id_{method_id}, result_is_object_{false}, dest_{dest}, args_{} {}
+  inline Instruction(Op opcode, size_t index_argument, std::optional<const Value> dest)
+      : opcode_{opcode}, index_argument_{index_argument}, result_is_object_{false}, dest_{dest}, args_{} {}
 
   template <typename... T>
-  inline constexpr Instruction(Op opcode, size_t method_id, bool result_is_object,
+  inline constexpr Instruction(Op opcode, size_t index_argument, bool result_is_object,
                                std::optional<const Value> dest, T... args)
       : opcode_{opcode},
-        method_id_{method_id},
+        index_argument_{index_argument},
         result_is_object_{result_is_object},
         dest_{dest},
         args_{args...} {}
 
   const Op opcode_;
   // The index of the method to invoke, for kInvokeVirtual and similar opcodes.
-  const size_t method_id_{0};
+  const size_t index_argument_{0};
   const bool result_is_object_;
   const std::optional<const Value> dest_;
   const std::vector<const Value> args_;
@@ -319,6 +331,7 @@
   void EncodeBranch(art::Instruction::Code op, const Instruction& instruction);
   void EncodeNew(const Instruction& instruction);
   void EncodeCast(const Instruction& instruction);
+  void EncodeStaticFieldOp(const Instruction& instruction);
 
   // Low-level instruction format encoding. See
   // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of
@@ -481,6 +494,11 @@
   // See the TypeDescriptor class for help generating these. GetOrAddType can be used to declare
   // imported classes.
   ir::Type* GetOrAddType(const std::string& descriptor);
+  inline ir::Type* GetOrAddType(TypeDescriptor descriptor) {
+    return GetOrAddType(descriptor.descriptor());
+  }
+
+  ir::FieldDecl* GetOrAddField(TypeDescriptor parent, const std::string& name, TypeDescriptor type);
 
   // Returns the method id for the method, creating it if it has not been created yet.
   const MethodDeclData& GetOrDeclareMethod(TypeDescriptor type, const std::string& name,
@@ -526,6 +544,9 @@
 
   // Keep track of already-encoded protos.
   std::map<Prototype, ir::Proto*> proto_map_;
+
+  // Keep track of fields that have been declared
+  std::map<std::tuple<TypeDescriptor, std::string>, ir::FieldDecl*> field_decls_by_key_;
 };
 
 template <typename... T>
diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp
index 22a3cfa..1214538 100644
--- a/startop/view_compiler/dex_builder_test/Android.bp
+++ b/startop/view_compiler/dex_builder_test/Android.bp
@@ -39,6 +39,7 @@
     srcs: [
         "src/android/startop/test/DexBuilderTest.java",
         "src/android/startop/test/LayoutCompilerTest.java",
+        "src/android/startop/test/TestClass.java",
     ],
     sdk_version: "current",
     data: [":generate_dex_testcases", ":generate_compiled_layout1", ":generate_compiled_layout2"],
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index f7b1674..6c0b8bb 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -24,10 +24,10 @@
 
 // Adding tests here requires changes in several other places. See README.md in
 // the view_compiler directory for more information.
-public class DexBuilderTest {
+public final class DexBuilderTest {
   static ClassLoader loadDexFile(String filename) throws Exception {
     return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename,
-        ClassLoader.getSystemClassLoader());
+        DexBuilderTest.class.getClassLoader());
   }
 
   public void hello() {}
@@ -167,4 +167,23 @@
     }
     Assert.assertTrue(castFailed);
   }
+
+  @Test
+  public void readStaticField() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("readStaticField");
+    TestClass.staticInteger = 5;
+    Assert.assertEquals(5, method.invoke(null));
+  }
+
+  @Test
+  public void setStaticField() throws Exception {
+    ClassLoader loader = loadDexFile("simple.dex");
+    Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
+    Method method = clazz.getMethod("setStaticField");
+    TestClass.staticInteger = 5;
+    method.invoke(null);
+    Assert.assertEquals(7, TestClass.staticInteger);
+  }
 }
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java
new file mode 100644
index 0000000..dd77923
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.startop.test;
+
+ /**
+ * A simple class to help test DexBuilder.
+ */
+public final class TestClass {
+    public static int staticInteger;
+
+    public int instanceField;
+}
diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc
index f62ec5dd..fee5e72 100644
--- a/startop/view_compiler/dex_testcase_generator.cc
+++ b/startop/view_compiler/dex_testcase_generator.cc
@@ -282,6 +282,37 @@
     method.Encode();
   }(castObjectToString);
 
+  // Read a static field
+  // integer readStaticField() { return TestClass.staticInteger; }
+  MethodBuilder readStaticField{
+      cbuilder.CreateMethod("readStaticField", Prototype{TypeDescriptor::Int()})};
+  [&](MethodBuilder& method) {
+    const ir::FieldDecl* field =
+        dex_file.GetOrAddField(TypeDescriptor::FromClassname("android.startop.test.TestClass"),
+                               "staticInteger",
+                               TypeDescriptor::Int());
+    Value result{method.MakeRegister()};
+    method.AddInstruction(Instruction::GetStaticField(field->orig_index, result));
+    method.BuildReturn(result, /*is_object=*/false);
+    method.Encode();
+  }(readStaticField);
+
+  // Set a static field
+  // void setStaticField() { TestClass.staticInteger = 7; }
+  MethodBuilder setStaticField{
+      cbuilder.CreateMethod("setStaticField", Prototype{TypeDescriptor::Void()})};
+  [&](MethodBuilder& method) {
+    const ir::FieldDecl* field =
+        dex_file.GetOrAddField(TypeDescriptor::FromClassname("android.startop.test.TestClass"),
+                               "staticInteger",
+                               TypeDescriptor::Int());
+    Value number{method.MakeRegister()};
+    method.BuildConst4(number, 7);
+    method.AddInstruction(Instruction::SetStaticField(field->orig_index, number));
+    method.BuildReturn();
+    method.Encode();
+  }(setStaticField);
+
   slicer::MemView image{dex_file.CreateImage()};
   std::ofstream out_file(outdir + "/simple.dex");
   out_file.write(image.ptr<const char>(), image.size());
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 91bea5f..cd79f37 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2151,6 +2151,15 @@
             "data_warning_notification_bool";
 
     /**
+     * Controls if the device should automatically warn the user that sim voice & data function
+     * might be limited due to dual sim scenario. When set to {@true} display the notification,
+     * {@code false} otherwise.
+     * @hide
+     */
+    public static final String KEY_LIMITED_SIM_FUNCTION_NOTIFICATION_FOR_DSDS_BOOL =
+            "limited_sim_function_notification_for_dsds_bool";
+
+    /**
      * Controls the cellular data limit.
      * <p>
      * If the user uses more than this amount of data in their billing cycle, as defined by
@@ -3029,6 +3038,23 @@
             "is_opportunistic_subscription_bool";
 
     /**
+     * Configs used by the IMS stack.
+     */
+    public static final class Ims {
+        /** Prefix of all Ims.KEY_* constants. */
+        public static final String KEY_PREFIX = "ims.";
+
+        //TODO: Add configs related to IMS.
+
+        private Ims() {}
+
+        private static PersistableBundle getDefaults() {
+            PersistableBundle defaults = new PersistableBundle();
+            return defaults;
+        }
+    }
+
+    /**
      * A list of 4 GSM RSSI thresholds above which a signal level is considered POOR,
      * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting.
      *
@@ -3326,6 +3352,7 @@
         sDefaults.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, DATA_CYCLE_USE_PLATFORM_DEFAULT);
         sDefaults.putLong(KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
         sDefaults.putBoolean(KEY_DATA_WARNING_NOTIFICATION_BOOL, true);
+        sDefaults.putBoolean(KEY_LIMITED_SIM_FUNCTION_NOTIFICATION_FOR_DSDS_BOOL, false);
         sDefaults.putLong(KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
         sDefaults.putBoolean(KEY_DATA_LIMIT_NOTIFICATION_BOOL, true);
         sDefaults.putBoolean(KEY_DATA_RAPID_NOTIFICATION_BOOL, true);
@@ -3463,6 +3490,7 @@
                         -89,  /* SIGNAL_STRENGTH_GREAT */
                 });
         sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
+        sDefaults.putAll(Ims.getDefaults());
     }
 
     /**
@@ -3659,4 +3687,75 @@
         return ICarrierConfigLoader.Stub
                 .asInterface(ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE));
     }
+
+    /**
+     * Gets the configuration values for a component using its prefix.
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *
+     * @param prefix prefix of the component.
+     * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
+     *
+     * @see #getConfigForSubId
+     */
+    @Nullable
+    public PersistableBundle getConfigByComponentForSubId(String prefix, int subId) {
+        PersistableBundle configs = getConfigForSubId(subId);
+
+        if (configs == null) {
+            return null;
+        }
+
+        PersistableBundle ret = new PersistableBundle();
+        for (String configKey : configs.keySet()) {
+            if (configKey.startsWith(prefix)) {
+                addConfig(configKey, configs.get(configKey), ret);
+            }
+        }
+
+        return ret;
+    }
+
+    private void addConfig(String key, Object value, PersistableBundle configs) {
+        if (value instanceof String) {
+            configs.putString(key, (String) value);
+        }
+
+        if (value instanceof String[]) {
+            configs.putStringArray(key, (String[]) value);
+        }
+
+        if (value instanceof Integer) {
+            configs.putInt(key, (Integer) value);
+        }
+
+        if (value instanceof Long) {
+            configs.putLong(key, (Long) value);
+        }
+
+        if (value instanceof Double) {
+            configs.putDouble(key, (Double) value);
+        }
+
+        if (value instanceof Boolean) {
+            configs.putBoolean(key, (Boolean) value);
+        }
+
+        if (value instanceof int[]) {
+            configs.putIntArray(key, (int[]) value);
+        }
+
+        if (value instanceof double[]) {
+            configs.putDoubleArray(key, (double[]) value);
+        }
+
+        if (value instanceof boolean[]) {
+            configs.putBooleanArray(key, (boolean[]) value);
+        }
+
+        if (value instanceof long[]) {
+            configs.putLongArray(key, (long[]) value);
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 9d732ba..fb16d54 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -143,7 +143,7 @@
     }
 
     /**
-     * Get the signal strength as dBm
+     * Get the signal strength as dBm.
      */
     @Override
     public int getDbm() {
@@ -163,18 +163,17 @@
     }
 
     /**
-     * Return the Received Signal Strength Indicator
+     * Return the Received Signal Strength Indicator.
      *
      * @return the RSSI in dBm (-113, -51) or
      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}.
-     * @hide
      */
     public int getRssi() {
         return mRssi;
     }
 
     /**
-     * Return the Bit Error Rate
+     * Return the Bit Error Rate.
      *
      * @return the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
      *         {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}.
diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java
index 8c686f7..8e1324b 100644
--- a/telephony/java/android/telephony/ims/ImsException.java
+++ b/telephony/java/android/telephony/ims/ImsException.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.pm.PackageManager;
+import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
@@ -55,12 +56,23 @@
      */
     public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2;
 
+    /**
+     * The subscription ID associated with this operation is invalid or not active.
+     * <p>
+     * This is a configuration error and there should be no retry. The subscription used for this
+     * operation is either invalid or has become inactive. The active subscriptions can be queried
+     * with {@link SubscriptionManager#getActiveSubscriptionInfoList()}.
+     * @hide
+     */
+    public static final int CODE_ERROR_INVALID_SUBSCRIPTION = 3;
+
     /**@hide*/
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "CODE_ERROR_", value = {
             CODE_ERROR_UNSPECIFIED,
             CODE_ERROR_SERVICE_UNAVAILABLE,
-            CODE_ERROR_UNSUPPORTED_OPERATION
+            CODE_ERROR_UNSUPPORTED_OPERATION,
+            CODE_ERROR_INVALID_SUBSCRIPTION
     })
     public @interface ImsErrorCode {}
 
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index be58723..a1a7fcc 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -31,6 +31,7 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.SubscriptionManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -375,6 +376,13 @@
         c.setExecutor(executor);
         try {
             getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException | IllegalStateException e) {
             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
@@ -390,8 +398,6 @@
      * @param c The {@link RegistrationCallback} to be removed.
      * @see SubscriptionManager.OnSubscriptionsChangedListener
      * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
-     * @throws IllegalArgumentException if the subscription ID associated with this callback is
-     * invalid.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
@@ -445,6 +451,13 @@
         c.setExecutor(executor);
         try {
             getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }  catch (IllegalStateException e) {
@@ -460,8 +473,6 @@
      * inactive subscription, it will result in a no-op.
      * @param c The MmTel {@link CapabilityCallback} to be removed.
      * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback)
-     * @throws IllegalArgumentException if the subscription ID associated with this callback is
-     * invalid.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
@@ -482,12 +493,9 @@
      * be enabled as long as the carrier has provisioned these services for the specified
      * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
      * carrier requirements.
-     *
-     * Modifying this value may also trigger an IMS registration or deregistration, depending on
-     * whether or not the new value is enabled or disabled.
-     *
+     * <p>
      * Note: If the carrier configuration for advanced calling is not editable or hidden, this
-     * method will do nothing and will instead always use the default value.
+     * method will always return the default value.
      *
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
      * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
@@ -495,12 +503,21 @@
      * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
      * @see #setAdvancedCallingSettingEnabled(boolean)
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return true if the user's setting for advanced calling is enabled, false otherwise.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isAdvancedCallingSettingEnabled() {
         try {
             return getITelephony().isAdvancedCallingSettingEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -526,12 +543,20 @@
      * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
      * @see #isAdvancedCallingSettingEnabled()
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setAdvancedCallingSettingEnabled(boolean isEnabled) {
         try {
             getITelephony().setAdvancedCallingSettingEnabled(mSubId, isEnabled);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -597,6 +622,9 @@
 
     /**
      * The user's setting for whether or not they have enabled the "Video Calling" setting.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return true if the user’s “Video Calling” setting is currently enabled.
      * @see #setVtSettingEnabled(boolean)
      */
@@ -604,6 +632,13 @@
     public boolean isVtSettingEnabled() {
         try {
             return getITelephony().isVtSettingEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -611,13 +646,22 @@
 
     /**
      * Change the user's setting for Video Telephony and enable the Video Telephony capability.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #isVtSettingEnabled()
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVtSettingEnabled(boolean isEnabled) {
         try {
             getITelephony().setVtSettingEnabled(mSubId, isEnabled);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -625,12 +669,22 @@
 
     /**
      * @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #setVoWiFiSettingEnabled(boolean)
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isVoWiFiSettingEnabled() {
         try {
             return getITelephony().isVoWiFiSettingEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -638,6 +692,9 @@
 
     /**
      * Sets the user's setting for whether or not Voice over WiFi is enabled.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
      * @see #isVoWiFiSettingEnabled()
      */
@@ -645,13 +702,23 @@
     public void setVoWiFiSettingEnabled(boolean isEnabled) {
         try {
             getITelephony().setVoWiFiSettingEnabled(mSubId, isEnabled);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
     }
 
     /**
+     * Returns the user's voice over WiFi roaming setting associated with the current subscription.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
      * if disabled.
      * @see #setVoWiFiRoamingSettingEnabled(boolean)
@@ -660,6 +727,13 @@
     public boolean isVoWiFiRoamingSettingEnabled() {
         try {
             return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -667,15 +741,24 @@
 
     /**
      * Change the user's setting for Voice over WiFi while roaming.
+     *
      * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
      *     false otherwise.
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #isVoWiFiRoamingSettingEnabled()
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) {
         try {
             getITelephony().setVoWiFiRoamingSettingEnabled(mSubId, isEnabled);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -691,19 +774,31 @@
      * - {@link #WIFI_MODE_WIFI_ONLY}
      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #setVoWiFiSettingEnabled(boolean)
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
         try {
             getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
     }
 
     /**
+     * Returns the user's voice over WiFi Roaming mode setting associated with the device.
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return The Voice over WiFi Mode preference set by the user, which can be one of the
      * following:
      * - {@link #WIFI_MODE_WIFI_ONLY}
@@ -715,6 +810,13 @@
     public @WiFiCallingMode int getVoWiFiModeSetting() {
         try {
             return getITelephony().getVoWiFiModeSetting(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -727,13 +829,21 @@
      * - {@link #WIFI_MODE_WIFI_ONLY}
      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #getVoWiFiModeSetting()
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
         try {
             getITelephony().setVoWiFiModeSetting(mSubId, mode);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -748,12 +858,21 @@
      *     - {@link #WIFI_MODE_WIFI_ONLY}
      *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #setVoWiFiRoamingSettingEnabled(boolean)
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
         try {
             return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -768,13 +887,21 @@
      *     - {@link #WIFI_MODE_WIFI_ONLY}
      *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see #getVoWiFiRoamingModeSetting()
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
         try {
             getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -787,13 +914,21 @@
      * {@link android.provider.Settings.Secure#RTT_CALLING_MODE}, which is the global user setting
      * for RTT. That value is enabled/disabled separately by the user through the Accessibility
      * settings.
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @param isEnabled if true RTT should be enabled during calls made on this subscription.
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setRttCapabilitySetting(boolean isEnabled) {
         try {
             getITelephony().setRttCapabilitySetting(mSubId, isEnabled);
-            return;
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -801,6 +936,9 @@
 
     /**
      * @return true if TTY over VoLTE is supported
+     *
+     * @throws IllegalArgumentException if the subscription associated with this operation is not
+     * active (SIM is not inserted, ESIM inactive) or invalid.
      * @see android.telecom.TelecomManager#getCurrentTtyMode
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
      */
@@ -808,6 +946,13 @@
     boolean isTtyOverVolteEnabled() {
         try {
             return getITelephony().isTtyOverVolteEnabled(mSubId);
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
+                // Rethrow as runtime error to keep API compatible.
+                throw new IllegalArgumentException(e.getMessage());
+            } else {
+                throw new RuntimeException(e.getMessage());
+            }
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
new file mode 100644
index 0000000..3c343dd
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.os.Binder;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Manager for interfacing with the framework RCS services, including the User Capability Exchange
+ * (UCE) service, as well as managing user settings.
+ *
+ * Use {@link #createForSubscriptionId(Context, int)} to create an instance of this manager.
+ * @hide
+ */
+public class ImsRcsManager {
+
+    /**
+     * Receives RCS availability status updates from the ImsService.
+     *
+     * @see #isAvailable(int)
+     * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
+     * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+     */
+    public static class AvailabilityCallback {
+
+        private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+
+            private final AvailabilityCallback mLocalCallback;
+            private Executor mExecutor;
+
+            CapabilityBinder(AvailabilityCallback c) {
+                mLocalCallback = c;
+            }
+
+            @Override
+            public void onCapabilitiesStatusChanged(int config) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged(
+                                new RcsFeature.RcsImsCapabilities(config))));
+            }
+
+            @Override
+            public void onQueryCapabilityConfiguration(int capability, int radioTech,
+                    boolean isEnabled) {
+                // This is not used for public interfaces.
+            }
+
+            @Override
+            public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                    @ImsFeature.ImsCapabilityError int reason) {
+                // This is not used for public interfaces
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final CapabilityBinder mBinder = new CapabilityBinder(this);
+
+        /**
+         * The availability of the feature's capabilities has changed to either available or
+         * unavailable.
+         * <p>
+         * If unavailable, the feature does not support the capability at the current time. This may
+         * be due to network or subscription provisioning changes, such as the IMS registration
+         * being lost, network type changing, or OMA-DM provisioning updates.
+         *
+         * @param capabilities The new availability of the capabilities.
+         */
+        public void onAvailabilityChanged(RcsFeature.RcsImsCapabilities capabilities) {
+        }
+
+        /**@hide*/
+        public final IImsCapabilityCallback getBinder() {
+            return mBinder;
+        }
+
+        private void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+    }
+
+    private final int mSubId;
+    private final Context mContext;
+
+
+    /**
+     * Create an instance of ImsRcsManager for the subscription id specified.
+     *
+     * @param context The context to create this ImsRcsManager instance within.
+     * @param subscriptionId The ID of the subscription that this ImsRcsManager will use.
+     * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+     * @throws IllegalArgumentException if the subscription is invalid.
+     * @hide
+     */
+    public static ImsRcsManager createForSubscriptionId(Context context, int subscriptionId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
+            throw new IllegalArgumentException("Invalid subscription ID");
+        }
+
+        return new ImsRcsManager(context, subscriptionId);
+    }
+
+    /**
+     * Use {@link #createForSubscriptionId(Context, int)} to create an instance of this class.
+     */
+    private ImsRcsManager(Context context, int subId) {
+        mContext = context;
+        mSubId = subId;
+    }
+
+    /**
+     * Registers an {@link AvailabilityCallback} with the system, which will provide RCS
+     * availability updates for the subscription specified.
+     *
+     * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
+     * subscription changed events and call
+     * {@link #unregisterRcsAvailabilityCallback(AvailabilityCallback)} to clean up after a
+     * subscription is removed.
+     * <p>
+     * When the callback is registered, it will initiate the callback c to be called with the
+     * current capabilities.
+     *
+     * @param executor The executor the callback events should be run on.
+     * @param c The RCS {@link AvailabilityCallback} to be registered.
+     * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void registerRcsAvailabilityCallback(@CallbackExecutor Executor executor,
+            @NonNull AvailabilityCallback c) throws ImsException {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        c.setExecutor(executor);
+        throw new UnsupportedOperationException("registerRcsAvailabilityCallback is not"
+                + "supported.");
+    }
+
+    /**
+     * Removes an existing RCS {@link AvailabilityCallback}.
+     * <p>
+     * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+     * etc...), this callback will automatically be unregistered. If this method is called for an
+     * inactive subscription, it will result in a no-op.
+     * @param c The RCS {@link AvailabilityCallback} to be removed.
+     * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+        }
+        throw new UnsupportedOperationException("unregisterRcsAvailabilityCallback is not"
+                + "supported.");
+    }
+
+    /**
+     * Query for the capability of an IMS RCS service provided by the framework.
+     * <p>
+     * This only reports the status of RCS capabilities provided by the framework, not necessarily
+     * RCS capabilities provided over-the-top by applications.
+     *
+     * @param capability The RCS capability to query.
+     * @return true if the RCS capability is capable for this subscription, false otherwise. This
+     * does not necessarily mean that we are registered for IMS and the capability is available, but
+     * rather the subscription is capable of this service over IMS.
+     * @see #isAvailable(int)
+     * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+        throw new UnsupportedOperationException("isCapable is not supported.");
+    }
+
+    /**
+     * Query the availability of an IMS RCS capability.
+     * <p>
+     * This only reports the status of RCS capabilities provided by the framework, not necessarily
+     * RCS capabilities provided by over-the-top by applications.
+     *
+     * @param capability the RCS capability to query.
+     * @return true if the RCS capability is currently available for the associated subscription,
+     * false otherwise. If the capability is available, IMS is registered and the service is
+     * currently available over IMS.
+     * @see #isCapable(int)
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isAvailable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+        throw new UnsupportedOperationException("isAvailable is not supported.");
+    }
+
+    /**
+     * @return A new {@link RcsUceAdapter} used for User Capability Exchange (UCE) operations for
+     * this subscription.
+     */
+    @NonNull
+    public RcsUceAdapter getUceAdapter() {
+        return new RcsUceAdapter(mSubId);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index cc037e3..effdf48 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -387,6 +387,24 @@
         }
     }
 
+    /**
+     * Notify the framework that an RCS autoconfiguration XML file has been received for
+     * provisioning.
+     * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed.
+     * @param isCompressed The XML file is compressed in gzip format and must be decompressed
+     *         before being read.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) {
+        if (config == null) {
+            throw new IllegalArgumentException("Must include a non-null config XML file.");
+        }
+        // TODO: Connect to ImsConfigImplBase.
+        throw new UnsupportedOperationException("notifyRcsAutoConfigurationReceived is not"
+                + "supported");
+    }
+
     private static boolean isImsAvailableOnDevice() {
         IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
         if (pm == null) {
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.aidl
similarity index 61%
copy from cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java
copy to telephony/java/android/telephony/ims/RcsContactUceCapability.aidl
index e000918..bef6a40 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (c) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,15 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.statsd.loadtest;
 
-import android.annotation.Nullable;
 
-public interface PerfParser {
+package android.telephony.ims;
 
-  /**
-   * Parses one line of the dumpsys output, and returns a string to write to the data file,
-   * or null if no string should be written.
-   */
-  @Nullable String parseLine(String line);
-}
+parcelable RcsContactUceCapability;
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
new file mode 100644
index 0000000..492170b
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Contains the User Capability Exchange capabilities corresponding to a contact's URI.
+ * @hide
+ */
+public final class RcsContactUceCapability implements Parcelable {
+
+    /** Supports 1-to-1 chat */
+    public static final int CAPABILITY_CHAT_STANDALONE = (1 << 0);
+    /** Supports group chat */
+    public static final int CAPABILITY_CHAT_SESSION = (1 << 1);
+    /** Supports full store and forward group chat information. */
+    public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = (1 << 2);
+    /**
+     * Supports file transfer via Message Session Relay Protocol (MSRP) without Store and Forward.
+     */
+    public static final int CAPABILITY_FILE_TRANSFER = (1 << 3);
+    /** Supports File Transfer Thumbnail */
+    public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = (1 << 4);
+    /** Supports File Transfer with Store and Forward */
+    public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = (1 << 5);
+    /** Supports File Transfer via HTTP */
+    public static final int CAPABILITY_FILE_TRANSFER_HTTP = (1 << 6);
+    /** Supports file transfer via SMS */
+    public static final int CAPABILITY_FILE_TRANSFER_SMS = (1 << 7);
+    /** Supports image sharing */
+    public static final int CAPABILITY_IMAGE_SHARE = (1 << 8);
+    /** Supports video sharing during a circuit-switch call (IR.74)*/
+    public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = (1 << 9);
+    /** Supports video share outside of voice call (IR.84) */
+    public static final int CAPABILITY_VIDEO_SHARE = (1 << 10);
+    /** Supports social presence information */
+    public static final int CAPABILITY_SOCIAL_PRESENCE = (1 << 11);
+    /** Supports capability discovery via presence */
+    public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = (1 << 12);
+    /** Supports IP Voice calling over LTE or IWLAN (IR.92/IR.51) */
+    public static final int CAPABILITY_IP_VOICE_CALL = (1 << 13);
+    /** Supports IP video calling (IR.94) */
+    public static final int CAPABILITY_IP_VIDEO_CALL = (1 << 14);
+    /** Supports Geolocation PUSH during 1-to-1 or multiparty chat */
+    public static final int CAPABILITY_GEOLOCATION_PUSH = (1 << 15);
+    /** Supports Geolocation PUSH via SMS for fallback.  */
+    public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = (1 << 16);
+    /** Supports Geolocation pull. */
+    public static final int CAPABILITY_GEOLOCATION_PULL = (1 << 17);
+    /** Supports Geolocation pull using file transfer support. */
+    public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = (1 << 18);
+    /** Supports RCS voice calling */
+    public static final int CAPABILITY_RCS_VOICE_CALL = (1 << 19);
+    /** Supports RCS video calling */
+    public static final int CAPABILITY_RCS_VIDEO_CALL = (1 << 20);
+    /** Supports RCS video calling, where video media can not be dropped */
+    public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = (1 << 21);
+
+    /** @hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CAPABILITY_", flag = true, value = {
+            CAPABILITY_CHAT_STANDALONE,
+            CAPABILITY_CHAT_SESSION,
+            CAPABILITY_CHAT_SESSION_STORE_FORWARD,
+            CAPABILITY_FILE_TRANSFER,
+            CAPABILITY_FILE_TRANSFER_THUMBNAIL,
+            CAPABILITY_FILE_TRANSFER_STORE_FORWARD,
+            CAPABILITY_FILE_TRANSFER_HTTP,
+            CAPABILITY_FILE_TRANSFER_SMS,
+            CAPABILITY_IMAGE_SHARE,
+            CAPABILITY_VIDEO_SHARE_DURING_CS_CALL,
+            CAPABILITY_VIDEO_SHARE,
+            CAPABILITY_SOCIAL_PRESENCE,
+            CAPABILITY_DISCOVERY_VIA_PRESENCE,
+            CAPABILITY_IP_VOICE_CALL,
+            CAPABILITY_IP_VIDEO_CALL,
+            CAPABILITY_GEOLOCATION_PUSH,
+            CAPABILITY_GEOLOCATION_PUSH_SMS,
+            CAPABILITY_GEOLOCATION_PULL,
+            CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER,
+            CAPABILITY_RCS_VOICE_CALL,
+            CAPABILITY_RCS_VIDEO_CALL,
+            CAPABILITY_RCS_VIDEO_ONLY_CALL
+    })
+    public @interface CapabilityFlag {}
+
+    /**
+     * Builder to help construct {@link RcsContactUceCapability} instances.
+     */
+    public static class Builder {
+
+        private final RcsContactUceCapability mCapabilities;
+
+        /**
+         * Create the Builder, which can be used to set UCE capabilities as well as custom
+         * capability extensions.
+         * @param contact The contact URI that the capabilities are attached to.
+         */
+        public Builder(@NonNull Uri contact) {
+            mCapabilities = new RcsContactUceCapability(contact);
+        }
+
+        /**
+         * Add a UCE capability bit-field as well as the associated URI that the framework should
+         * use for those services. This is mainly used for capabilities that may use a URI separate
+         * from the contact's URI, for example the URI to use for VT calls.
+         * @param type The capability to map to a service URI that is different from the contact's
+         *         URI.
+         */
+        public Builder add(@CapabilityFlag int type, @NonNull Uri serviceUri) {
+            mCapabilities.mCapabilities |= type;
+            // Put each of these capabilities into the map separately.
+            for (int shift = 0; shift < Integer.SIZE; shift++) {
+                int cap = type & (1 << shift);
+                if (cap != 0) {
+                    mCapabilities.mServiceMap.put(cap, serviceUri);
+                    // remove that capability from the field.
+                    type &= ~cap;
+                }
+                if (type == 0) {
+                    // no need to keep going, end early.
+                    break;
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Add a UCE capability flag that this contact supports.
+         * @param type the capability that the contact supports.
+         */
+        public Builder add(@CapabilityFlag int type) {
+            mCapabilities.mCapabilities |= type;
+            return this;
+        }
+
+        /**
+         * Add a carrier specific service tag.
+         * @param extension A string containing a carrier specific service tag that is an extension
+         *         of the {@link CapabilityFlag}s that are defined here.
+         */
+        public Builder add(@NonNull String extension) {
+            mCapabilities.mExtensionTags.add(extension);
+            return this;
+        }
+
+        /**
+         * @return the constructed instance.
+         */
+        public RcsContactUceCapability build() {
+            return mCapabilities;
+        }
+    }
+
+    private final Uri mContactUri;
+    private int mCapabilities;
+    private List<String> mExtensionTags = new ArrayList<>();
+    private Map<Integer, Uri> mServiceMap = new HashMap<>();
+
+    /**
+     * Use {@link Builder} to build an instance of this interface.
+     * @param contact The URI associated with this capability information.
+     * @hide
+     */
+    RcsContactUceCapability(@NonNull Uri contact) {
+        mContactUri = contact;
+    }
+
+    private RcsContactUceCapability(Parcel in) {
+        mContactUri = in.readParcelable(Uri.class.getClassLoader());
+        mCapabilities = in.readInt();
+        in.readStringList(mExtensionTags);
+        // read mServiceMap as key,value pair
+        int mapSize = in.readInt();
+        for (int i = 0; i < mapSize; i++) {
+            mServiceMap.put(in.readInt(), in.readParcelable(Uri.class.getClassLoader()));
+        }
+    }
+
+    public static final Creator<RcsContactUceCapability> CREATOR =
+            new Creator<RcsContactUceCapability>() {
+        @Override
+        public RcsContactUceCapability createFromParcel(Parcel in) {
+            return new RcsContactUceCapability(in);
+        }
+
+        @Override
+        public RcsContactUceCapability[] newArray(int size) {
+            return new RcsContactUceCapability[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(mContactUri, 0);
+        out.writeInt(mCapabilities);
+        out.writeStringList(mExtensionTags);
+        // write mServiceMap as key,value pairs
+        int mapSize = mServiceMap.keySet().size();
+        out.writeInt(mapSize);
+        for (int key : mServiceMap.keySet()) {
+            out.writeInt(key);
+            out.writeParcelable(mServiceMap.get(key), 0);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Query for a capability
+     * @param type The capability flag to query.
+     * @return true if the capability flag specified is set, false otherwise.
+     */
+    public boolean isCapable(@CapabilityFlag int type) {
+        return (mCapabilities & type) > 0;
+    }
+
+    /**
+     * @return true if the extension service tag is set, false otherwise.
+     */
+    public boolean isCapable(@NonNull String extensionTag) {
+        return mExtensionTags.contains(extensionTag);
+    }
+
+    /**
+     * @return An immutable list containing all of the extension tags that have been set as capable.
+     * @throws UnsupportedOperationException if this list is modified.
+     */
+    public @NonNull List<String> getCapableExtensionTags() {
+        return Collections.unmodifiableList(mExtensionTags);
+    }
+
+    /**
+     * Retrieves the {@link Uri} associated with the capability being queried.
+     * <p>
+     * This will typically be the contact {@link Uri} available via {@link #getContactUri()} unless
+     * a different service {@link Uri} was associated with this capability using
+     * {@link Builder#add(int, Uri)}.
+     *
+     * @return a String containing the {@link Uri} associated with the service tag or
+     * {@code null} if this capability is not set as capable.
+     * @see #isCapable(int)
+     */
+    public @Nullable Uri getServiceUri(@CapabilityFlag int type) {
+        Uri result = mServiceMap.getOrDefault(type, null);
+        // If the capability is capable, but does not have a service URI associated, use the default
+        // contact URI.
+        if (result == null) {
+            return isCapable(type) ? getContactUri() : null;
+        }
+        return result;
+    }
+
+    /**
+     * @return the URI representing the contact associated with the capabilities.
+     */
+    public @NonNull Uri getContactUri() {
+        return mContactUri;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
new file mode 100644
index 0000000..a6a7a84
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.net.Uri;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages RCS User Capability Exchange for the subscription specified.
+ *
+ * @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class.
+ * @hide
+ */
+public class RcsUceAdapter {
+
+    /**
+     * An unknown error has caused the request to fail.
+     */
+    public static final int ERROR_GENERIC_FAILURE = 1;
+    /**
+     * The carrier network does not have UCE support enabled for this subscriber.
+     */
+    public static final int ERROR_NOT_ENABLED = 2;
+    /**
+     * The data network that the device is connected to does not support UCE currently (e.g. it is
+     * 1x only currently).
+     */
+    public static final int ERROR_NOT_AVAILABLE = 3;
+    /**
+     * The network has responded with SIP 403 error and a reason "User not registered."
+     */
+    public static final int ERROR_NOT_REGISTERED = 4;
+    /**
+     * The network has responded to this request with a SIP 403 error and reason "not authorized for
+     * presence" for this subscriber.
+     */
+    public static final int ERROR_NOT_AUTHORIZED = 5;
+    /**
+     * The network has responded to this request with a SIP 403 error and no reason.
+     */
+    public static final int ERROR_FORBIDDEN = 6;
+    /**
+     * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS
+     * subscriber to the carrier network.
+     */
+    public static final int ERROR_NOT_FOUND = 7;
+    /**
+     * The capabilities request contained too many URIs for the carrier network to handle. Retry
+     * with a lower number of contact numbers. The number varies per carrier.
+     */
+    // TODO: Try to integrate this into the API so that the service will split based on carrier.
+    public static final int ERROR_REQUEST_TOO_LARGE = 8;
+    /**
+     * The network did not respond to the capabilities request before the request timed out.
+     */
+    public static final int ERROR_REQUEST_TIMEOUT = 10;
+    /**
+     * The request failed due to the service having insufficient memory.
+     */
+    public static final int ERROR_INSUFFICIENT_MEMORY = 11;
+    /**
+     * The network was lost while trying to complete the request.
+     */
+    public static final int ERROR_LOST_NETWORK = 12;
+    /**
+     * The request has failed because the same request has already been added to the queue.
+     */
+    public static final int ERROR_ALREADY_IN_QUEUE = 13;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "ERROR_", value = {
+            ERROR_GENERIC_FAILURE,
+            ERROR_NOT_ENABLED,
+            ERROR_NOT_AVAILABLE,
+            ERROR_NOT_REGISTERED,
+            ERROR_NOT_AUTHORIZED,
+            ERROR_FORBIDDEN,
+            ERROR_NOT_FOUND,
+            ERROR_REQUEST_TOO_LARGE,
+            ERROR_REQUEST_TIMEOUT,
+            ERROR_INSUFFICIENT_MEMORY,
+            ERROR_LOST_NETWORK,
+            ERROR_ALREADY_IN_QUEUE
+    })
+    public @interface ErrorCode {}
+
+    /**
+     * The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for
+     * UCE.
+     */
+    public static final int PUBLISH_STATE_200_OK = 1;
+
+    /**
+     * The hasn't published its capabilities since boot or hasn't gotten any publish response yet.
+     */
+    public static final int PUBLISH_STATE_NOT_PUBLISHED = 2;
+
+    /**
+     * The device has tried to publish its capabilities, which has resulted in an error. This error
+     * is related to the fact that the device is not VoLTE provisioned.
+     */
+    public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3;
+
+    /**
+     * The device has tried to publish its capabilities, which has resulted in an error. This error
+     * is related to the fact that the device is not RCS or UCE provisioned.
+     */
+    public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4;
+
+    /**
+     * The last publish resulted in a "408 Request Timeout" response.
+     */
+    public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5;
+
+    /**
+     * The last publish resulted in another unknown error, such as SIP 503 - "Service Unavailable"
+     * or SIP 423 - "Interval too short".
+     * <p>
+     * Device shall retry with exponential back-off.
+     */
+    public static final int PUBLISH_STATE_OTHER_ERROR = 6;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "PUBLISH_STATE_", value = {
+            PUBLISH_STATE_200_OK,
+            PUBLISH_STATE_NOT_PUBLISHED,
+            PUBLISH_STATE_VOLTE_PROVISION_ERROR,
+            PUBLISH_STATE_RCS_PROVISION_ERROR,
+            PUBLISH_STATE_REQUEST_TIMEOUT,
+            PUBLISH_STATE_OTHER_ERROR
+    })
+    public @interface PublishState {}
+
+
+    /**
+     * Provides a one-time callback for the response to a UCE request. After this callback is called
+     * by the framework, the reference to this callback will be discarded on the service side.
+     * @see #requestCapabilities(Executor, List, CapabilitiesCallback)
+     */
+    public static class CapabilitiesCallback {
+
+        /**
+         * Notify this application that the pending capability request has returned successfully.
+         * @param contactCapabilities List of capabilities associated with each contact requested.
+         */
+        public void onCapabilitiesReceived(
+                @NonNull List<RcsContactUceCapability> contactCapabilities) {
+
+        }
+
+        /**
+         * The pending request has resulted in an error and may need to be retried, depending on the
+         * error code.
+         * @param errorCode The reason for the framework being unable to process the request.
+         */
+        public void onError(@ErrorCode int errorCode) {
+
+        }
+    }
+
+    private final int mSubId;
+
+    /**
+     * Not to be instantiated directly, use
+     * {@link ImsRcsManager#createForSubscriptionId(Context, int)} and
+     * {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class.
+     */
+    RcsUceAdapter(int subId) {
+        mSubId = subId;
+    }
+
+    /**
+     * Request the User Capability Exchange capabilities for one or more contacts.
+     * <p>
+     * Be sure to check the availability of this feature using
+     * {@link ImsRcsManager#isAvailable(int)} and ensuring
+     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or
+     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else
+     * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
+     *
+     * @param executor The executor that will be used when the request is completed and the
+     *         {@link CapabilitiesCallback} is called.
+     * @param contactNumbers A list of numbers that the capabilities are being requested for.
+     * @param c A one-time callback for when the request for capabilities completes or there is an
+     *         error processing the request.
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void requestCapabilities(@CallbackExecutor Executor executor,
+            @NonNull List<Uri> contactNumbers,
+            @NonNull CapabilitiesCallback c) throws ImsException {
+        throw new UnsupportedOperationException("isUceSettingEnabled is not supported.");
+    }
+
+    /**
+     * Gets the last publish result from the UCE service if the device is using an RCS presence
+     * server.
+     * @return The last publish result from the UCE service. If the device is using SIP OPTIONS,
+     * this method will return {@link #PUBLISH_STATE_200_OK} as well.
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @PublishState int getUcePublishState() throws ImsException {
+        throw new UnsupportedOperationException("getPublishState is not supported.");
+    }
+
+    /**
+     * The user’s setting for whether or not Presence and User Capability Exchange (UCE) is enabled
+     * for the associated subscription.
+     *
+     * @return true if the user’s setting for UCE is enabled, false otherwise. If false,
+     * {@link ImsRcsManager#isCapable(int)} will return false for
+     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and
+     * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE}
+     * @see #setUceSettingEnabled(boolean)
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isUceSettingEnabled() throws ImsException {
+        // TODO: add SubscriptionController column for this property.
+        throw new UnsupportedOperationException("isUceSettingEnabled is not supported.");
+    }
+    /**
+     * Change the user’s setting for whether or not UCE is enabled for the associated subscription.
+     * @param isEnabled the user's setting for whether or not they wish for Presence and User
+     *         Capability Exchange to be enabled. If false,
+     *         {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and
+     *         {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} capability will be
+     *         disabled, depending on which type of UCE the carrier supports.
+     * @see #isUceSettingEnabled()
+     * @throws ImsException if the subscription associated with this instance of
+     * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
+     * available. This can happen if the ImsService has crashed, for example, or if the subscription
+     * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setUceSettingEnabled(boolean isEnabled) throws ImsException {
+        // TODO: add SubscriptionController column for this property.
+        throw new UnsupportedOperationException("setUceSettingEnabled is not supported.");
+    }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
index 4433c1c..53e4596 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl
@@ -17,6 +17,8 @@
 
 package android.telephony.ims.aidl;
 
+import android.os.PersistableBundle;
+
 import android.telephony.ims.aidl.IImsConfigCallback;
 
 import com.android.ims.ImsConfigListener;
@@ -37,4 +39,5 @@
     int setConfigInt(int item, int value);
     // Return result code defined in ImsConfig#OperationStatusConstants
     int setConfigString(int item, String value);
+    void updateImsCarrierConfigs(in PersistableBundle bundle);
 }
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index a637e16..5fae3ee 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -16,8 +16,14 @@
 
 package android.telephony.ims.feature;
 
+import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
+import android.telephony.ims.stub.RcsSipOptionsImplBase;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
@@ -32,18 +38,165 @@
         // Empty Default Implementation.
     };
 
+    /**
+     * Contains the capabilities defined and supported by a {@link RcsFeature} in the
+     * form of a bitmask. The capabilities that are used in the RcsFeature are
+     * defined as:
+     * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE}
+     * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE}
+     *
+     * The enabled capabilities of this RcsFeature will be set by the framework
+     * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}.
+     * After the capabilities have been set, the RcsFeature may then perform the necessary bring up
+     * of the capability and notify the capability status as true using
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
+     * framework that the capability is available for usage.
+     * @hide
+     */
+    public static class RcsImsCapabilities extends Capabilities {
+        /** @hide*/
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = {
+                CAPABILITY_TYPE_OPTIONS_UCE,
+                CAPABILITY_TYPE_PRESENCE_UCE
+        })
+        public @interface RcsImsCapabilityFlag {}
 
-    public RcsFeature() {
-        super();
+        /**
+         * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
+         * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
+         * If not set, this RcsFeature should not service capability requests.
+         * @hide
+         */
+        public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
+
+        /**
+         * This carrier supports User Capability Exchange using a presence server as defined by the
+         * framework. If set, the RcsFeature should support capability exchange using a presence
+         * server. If not set, this RcsFeature should not publish capabilities or service capability
+         * requests using presence.
+         * @hide
+         */
+        public static final int CAPABILITY_TYPE_PRESENCE_UCE =  1 << 1;
+
+        /**@hide*/
+        public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) {
+
+        }
+
+        /**@hide*/
+        @Override
+        public void addCapabilities(@RcsImsCapabilityFlag int capabilities) {
+
+        }
+
+        /**@hide*/
+        @Override
+        public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) {
+
+        }
+
+        /**@hide*/
+        @Override
+        public boolean isCapable(@RcsImsCapabilityFlag int capabilities) {
+            return false;
+        }
+    }
+    /**
+     * Query the current {@link RcsImsCapabilities} status set by the RcsFeature. If a capability is
+     * set, the {@link RcsFeature} has brought up the capability and is ready for framework
+     * requests. To change the status of the capabilities
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
+     * @hide
+     */
+    @Override
+    public final RcsImsCapabilities queryCapabilityStatus() {
+        throw new UnsupportedOperationException();
     }
 
     /**
-     * {@inheritDoc}
+     * Notify the framework that the capabilities status has changed. If a capability is enabled,
+     * this signals to the framework that the capability has been initialized and is ready.
+     * Call {@link #queryCapabilityStatus()} to return the current capability status.
+     * @hide
+     */
+    public final void notifyCapabilitiesStatusChanged(RcsImsCapabilities c) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Provides the RcsFeature with the ability to return the framework capability configuration set
+     * by the framework. When the framework calls
+     * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to
+     * enable or disable capability A, this method should return the correct configuration for
+     * capability A afterwards (until it has changed).
+     * @hide
+     */
+    public boolean queryCapabilityConfiguration(
+            @RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+        throw new UnsupportedOperationException();
+    }
+    /**
+     * Called from the framework when the {@link RcsImsCapabilities} that have been configured for
+     * this {@link RcsFeature} has changed.
+     * <p>
+     * For each newly enabled capability flag, the corresponding capability should be brought up in
+     * the {@link RcsFeature} and registered on the network. For each newly disabled capability
+     * flag, the corresponding capability should be brought down, and deregistered. Once a new
+     * capability has been initialized and is ready for usage, the status of that capability should
+     * also be set to true using {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This
+     * will notify the framework that the capability is ready.
+     * <p>
+     * If for some reason one or more of these capabilities can not be enabled/disabled,
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should
+     * be called for each capability change that resulted in an error.
+     * @hide
      */
     @Override
     public void changeEnabledCapabilities(CapabilityChangeRequest request,
             CapabilityCallbackProxy c) {
-        // Do nothing for base implementation.
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Retrieve the implementation of SIP OPTIONS for this {@link RcsFeature}.
+     * <p>
+     * Will only be requested by the framework if capability exchange via SIP OPTIONS is
+     * configured as capable during a
+     * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
+     * operation and the RcsFeature sets the status of the capability to true using
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
+     *
+     * @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if
+     * it is supported by the device.
+     * @hide
+     */
+    public RcsSipOptionsImplBase getOptionsExchangeImpl() {
+        // Base Implementation, override to implement functionality
+        return new RcsSipOptionsImplBase();
+    }
+
+    /**
+     * Retrieve the implementation of UCE presence for this {@link RcsFeature}.
+     * Will only be requested by the framework if presence exchang is configured as capable during
+     * a {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}
+     * operation and the RcsFeature sets the status of the capability to true using
+     * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}.
+     *
+     * @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence
+     * exchange if it is supported by the device.
+     * @hide
+     */
+    public RcsPresenceExchangeImplBase getPresenceExchangeImpl() {
+        // Base Implementation, override to implement functionality.
+        return new RcsPresenceExchangeImplBase();
+    }
+
+    /**
+     * Construct a new {@link RcsFeature} instance.
+     */
+    public RcsFeature() {
+        super();
     }
 
     /**{@inheritDoc}*/
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 321bfff..3e135cc 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.os.PersistableBundle;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.telephony.ims.aidl.IImsConfig;
@@ -182,6 +183,11 @@
             return retVal;
         }
 
+        @Override
+        public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException {
+            getImsConfigImpl().updateImsCarrierConfigs(bundle);
+        }
+
         private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
             ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
             if (ref == null) {
@@ -342,6 +348,17 @@
     }
 
     /**
+     * The framework has received an RCS autoconfiguration XML file for provisioning.
+     *
+     * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format.
+     * @param isCompressed The XML file is compressed in gzip format and must be decompressed
+     *         before being read.
+     * @hide
+     */
+    public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) {
+    }
+
+    /**
      * Sets the configuration value for this ImsService.
      *
      * @param item an integer key.
@@ -387,4 +404,11 @@
         // Base Implementation - To be overridden.
         return null;
     }
+
+    /**
+     * @hide
+     */
+    public void updateImsCarrierConfigs(PersistableBundle bundle) {
+        // Base Implementation - Should be overridden
+    }
 }
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
new file mode 100644
index 0000000..289fd4c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base class for different types of Capability exchange, presence using
+ * {@link RcsPresenceExchangeImplBase} and SIP OPTIONS exchange using {@link RcsSipOptionsImplBase}.
+ *
+ * @hide
+ */
+public class RcsCapabilityExchange {
+
+    /**  Service is unknown. */
+    public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0;
+    /** The command completed successfully. */
+    public static final int COMMAND_CODE_SUCCESS = 1;
+    /** The command failed with an unknown error. */
+    public static final int COMMAND_CODE_GENERIC_FAILURE = 2;
+    /**  Invalid parameter(s). */
+    public static final int COMMAND_CODE_INVALID_PARAM = 3;
+    /**  Fetch error. */
+    public static final int COMMAND_CODE_FETCH_ERROR = 4;
+    /**  Request timed out. */
+    public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5;
+    /**  Failure due to insufficient memory available. */
+    public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6;
+    /**  Network connection is lost. */
+    public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7;
+    /**  Requested feature/resource is not supported. */
+    public static final int COMMAND_CODE_NOT_SUPPORTED = 8;
+    /**  Contact or resource is not found. */
+    public static final int COMMAND_CODE_NOT_FOUND = 9;
+    /**  Service is not available. */
+    public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10;
+    /**  No Change in Capabilities */
+    public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11;
+
+    /** @hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "COMMAND_CODE_", value = {
+            COMMAND_CODE_SERVICE_UNKNOWN,
+            COMMAND_CODE_SUCCESS,
+            COMMAND_CODE_GENERIC_FAILURE,
+            COMMAND_CODE_INVALID_PARAM,
+            COMMAND_CODE_FETCH_ERROR,
+            COMMAND_CODE_REQUEST_TIMEOUT,
+            COMMAND_CODE_INSUFFICIENT_MEMORY,
+            COMMAND_CODE_LOST_NETWORK_CONNECTION,
+            COMMAND_CODE_NOT_SUPPORTED,
+            COMMAND_CODE_NOT_FOUND,
+            COMMAND_CODE_SERVICE_UNAVAILABLE,
+            COMMAND_CODE_NO_CHANGE_IN_CAP
+    })
+    public @interface CommandCode {}
+
+    /**
+     * Provides the framework with an update as to whether or not a command completed successfully
+     * locally. This includes capabilities requests and updates from the network. If it does not
+     * complete successfully, then the framework may retry the command again later, depending on the
+     * error. If the command does complete successfully, the framework will then wait for network
+     * updates.
+     *
+     * @param code The result of the pending command. If {@link #COMMAND_CODE_SUCCESS}, further
+     *             updates will be sent for this command using the associated operationToken.
+     * @param operationToken the token associated with the pending command.
+     */
+    public final void onCommandUpdate(@CommandCode int code, int operationToken) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
new file mode 100644
index 0000000..4402470
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.telephony.ims.RcsContactUceCapability;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Base implementation for RCS User Capability Exchange using Presence. Any ImsService implementing
+ * this service must implement the stub methods {@link #requestCapabilities(List, int)}  and
+ * {@link #updateCapabilities(RcsContactUceCapability, int)}.
+ *
+ * @hide
+ */
+public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange {
+
+    private static final String LOG_TAG = "RcsPresenceExchangeIB";
+
+    /**
+     * The request has resulted in any other 4xx/5xx/6xx that is not covered below. No retry will be
+     * attempted.
+     */
+    public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1;
+
+    /**
+     * The request has succeeded with a “200” message from the network.
+     */
+    public static final int RESPONSE_SUCCESS = 0;
+
+    /**
+     * The request has resulted in a “403” (User Not Registered) error from the network. Will retry
+     * capability polling with an exponential backoff.
+     */
+    public static final int RESPONSE_NOT_REGISTERED = 1;
+
+    /**
+     * The request has resulted in a “403” (not authorized (Requestor)) error from the network. No
+     * retry will be attempted.
+     */
+    public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2;
+
+    /**
+     * The request has resulted in a "403” (Forbidden) or other “403” error from the network and
+     * will be handled the same as “404” Not found. No retry will be attempted.
+     */
+    public static final int RESPONSE_FORBIDDEN = 3;
+
+    /**
+     * The request has resulted in a “404” (Not found) result from the network. No retry will be
+     * attempted.
+     */
+    public static final int RESPONSE_NOT_FOUND = 4;
+
+    /**
+     * The request has resulted in a “408” response. Retry after exponential backoff.
+     */
+    public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5;
+
+    /**
+     *  The network has responded with a “413” (Too Large) response from the network. Capability
+     *  request contains too many items and must be shrunk before the request will be accepted.
+     */
+    public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6;
+
+    /**
+     * The request has resulted in a “423” response. Retry after exponential backoff.
+     */
+    public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7;
+
+    /**
+     * The request has resulted in a “503” response. Retry after exponential backoff.
+     */
+    public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8;
+
+    /** @hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "RESPONSE_", value = {
+            RESPONSE_SUBSCRIBE_GENERIC_FAILURE,
+            RESPONSE_SUCCESS,
+            RESPONSE_NOT_REGISTERED,
+            RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE,
+            RESPONSE_FORBIDDEN,
+            RESPONSE_NOT_FOUND,
+            RESPONSE_SIP_REQUEST_TIMEOUT,
+            RESPONSE_SUBSCRIBE_TOO_LARGE,
+            RESPONSE_SIP_INTERVAL_TOO_SHORT,
+            RESPONSE_SIP_SERVICE_UNAVAILABLE
+    })
+    public @interface PresenceResponseCode {}
+
+    /**
+     * Provide the framework with a subsequent network response update to
+     * {@link #updateCapabilities(RcsContactUceCapability, int)} and
+     * {@link #requestCapabilities(List, int)} operations.
+     * @param code The SIP response code sent from the network for the operation token specified.
+     * @param reason The optional reason response from the network. If the network provided no
+     *         reason with the code, the string should be empty.
+     * @param operationToken The token associated with the operation this service is providing a
+     *         response for.
+     */
+    public final void onNetworkResponse(@PresenceResponseCode int code, @NonNull String reason,
+            int operationToken) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Provides the framework with the requested contacts’ capabilities requested by the framework
+     * using {@link #requestCapabilities(List, int)} .
+     */
+    public final void onCapabilityRequestResponse(@NonNull List<RcsContactUceCapability> infos,
+            int operationToken) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Trigger the framework to provide a capability update using
+     * {@link #updateCapabilities(RcsContactUceCapability, int)}. This is typically used when trying
+     * to generate an initial PUBLISH for a new subscription to the network.
+     * <p>
+     * The device will cache all presence publications after boot until this method is called once.
+     */
+    public final void onNotifyUpdateCapabilites() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Notify the framework that the device’s capabilities have been unpublished from the network.
+     */
+    public final void onUnpublish() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * The user capabilities of one or multiple contacts have been requested.
+     * <p>
+     * This must be followed up with one call to {@link #onCommandUpdate(int, int)} with an update
+     * as to whether or not the command completed as well as subsequent network
+     * updates using {@link #onNetworkResponse(int, String, int)}. When the operation is completed,
+     * {@link #onCapabilityRequestResponse(List, int)}  should be called with
+     * the presence information for the contacts specified.
+     * @param uris A {@link List} of the URIs that the framework is requesting the UCE capabilities
+     *          for.
+     * @param operationToken The token associated with this operation. Updates to this request using
+     *         {@link #onCommandUpdate(int, int)}, {@link #onNetworkResponse(int, String, int)}, and
+     *         {@link #onCapabilityRequestResponse(List, int)}  must use the same operation token
+     *         in response.
+     */
+    public void requestCapabilities(@NonNull List<Uri> uris, int operationToken) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "requestCapabilities called with no implementation.");
+        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+    }
+
+    /**
+     * The capabilities of this device have been updated and should be published
+     * to the network. The framework will expect one {@link #onCommandUpdate(int, int)} call to
+     * indicate whether or not this operation failed first as well as network response
+     * updates to this update using {@link #onNetworkResponse(int, String, int)}.
+     * @param capabilities The capabilities for this device.
+     * @param operationToken The token associated with this operation. Any subsequent
+     *         {@link #onCommandUpdate(int, int)} or {@link #onNetworkResponse(int, String, int)}
+     *         calls regarding this update must use the same token.
+     */
+    public void updateCapabilities(@NonNull RcsContactUceCapability capabilities,
+            int operationToken) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "updateCapabilities called with no implementation.");
+        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+    }
+}
diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
new file mode 100644
index 0000000..3343074
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.stub;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.telephony.ims.RcsContactUceCapability;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base implementation for RCS User Capability Exchange using SIP OPTIONS.
+ *
+ * @hide
+ */
+public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
+
+    private static final String LOG_TAG = "RcsSipOptionsImplBase";
+
+    /**
+     * Indicates a SIP response from the remote user other than 200, 480, 408, 404, or 604.
+     */
+    public static final int RESPONSE_GENERIC_FAILURE = -1;
+
+    /**
+     * Indicates that the remote user responded with a 200 OK response.
+     */
+    public static final int RESPONSE_SUCCESS = 0;
+
+    /**
+     * Indicates that the remote user responded with a 480 TEMPORARY UNAVAILABLE response.
+     */
+    public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1;
+
+    /**
+     * Indicates that the remote user responded with a 408 REQUEST TIMEOUT response.
+     */
+    public static final int RESPONSE_REQUEST_TIMEOUT = 2;
+
+    /**
+     * Indicates that the remote user responded with a 404 NOT FOUND response.
+     */
+    public static final int RESPONSE_NOT_FOUND = 3;
+
+    /**
+     * Indicates that the remote user responded with a 604 DOES NOT EXIST ANYWHERE response.
+     */
+    public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4;
+
+    /** @hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "RESPONSE_", value = {
+            RESPONSE_GENERIC_FAILURE,
+            RESPONSE_SUCCESS,
+            RESPONSE_TEMPORARILY_UNAVAILABLE,
+            RESPONSE_REQUEST_TIMEOUT,
+            RESPONSE_NOT_FOUND,
+            RESPONSE_DOES_NOT_EXIST_ANYWHERE
+    })
+    public @interface SipResponseCode {}
+
+    /**
+     * Send the response of a SIP OPTIONS capability exchange to the framework. If {@code code} is
+     * {@link #RESPONSE_SUCCESS}, info must be non-null.
+     * @param code The SIP response code that was sent by the network in response to the request
+     *        sent by {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+     * @param reason The optional SIP response reason sent by the network. If none was sent, this
+     *        should be an empty string.
+     * @param info the contact's UCE capabilities associated with the capability request.
+     * @param operationToken The token associated with the original capability request, set by
+     *        {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+     */
+    public final void onCapabilityRequestResponse(@SipResponseCode int code, @NonNull String reason,
+            @Nullable RcsContactUceCapability info, int operationToken) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Inform the framework of a query for this device's UCE capabilities.
+     * <p>
+     * The framework will respond via the
+     * {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)} or
+     * {@link #respondToCapabilityRequestWithError(Uri, int, String, int)} method.
+     * @param contactUri The URI associated with the remote contact that is requesting capabilities.
+     * @param remoteInfo The remote contact's capability information.
+     * @param operationToken An unique operation token that you have generated that will be returned
+     *         by the framework in
+     *         {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)}.
+     */
+    public final void onRemoteCapabilityRequest(@NonNull Uri contactUri,
+            @NonNull RcsContactUceCapability remoteInfo, int operationToken) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism
+     * in order to receive the capabilities of the remote user in response.
+     * <p>
+     * The implementer must call
+     * {@link #onCapabilityRequestResponse(int, String, RcsContactUceCapability, int)} to send the
+     * response of this query back to the framework.
+     * @param contactUri The URI of the remote user that we wish to get the capabilities of.
+     * @param capabilities The capabilities of this device to send to the remote user.
+     * @param operationToken A token generated by the framework that will be passed through
+     * {@link #onCapabilityRequestResponse(int, String, RcsContactUceCapability, int)} when this
+     *         operation has succeeded.
+     */
+    public void sendCapabilityRequest(@NonNull Uri contactUri,
+            @NonNull RcsContactUceCapability capabilities, int operationToken) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "sendCapabilityRequest called with no implementation.");
+        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+    }
+
+    /**
+     * Respond to a remote capability request from the contact specified with the capabilities of
+     * this device.
+     * <p>
+     * The framework will use the same token and uri as what was passed in to
+     * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+     * @param contactUri The URI of the remote contact.
+     * @param ownCapabilities The capabilities of this device.
+     * @param operationToken The token generated by the framework that this service obtained when
+     *         {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called.
+     */
+    public void respondToCapabilityRequest(@NonNull String contactUri,
+            @NonNull RcsContactUceCapability ownCapabilities, int operationToken) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "respondToCapabilityRequest called with no implementation.");
+        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+    }
+
+    /**
+     * Respond to a remote capability request from the contact specified with the specified error.
+     * <p>
+     * The framework will use the same token and uri as what was passed in to
+     * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+     * @param contactUri A URI containing the remote contact.
+     * @param code The SIP response code to respond with.
+     * @param reason A non-null String containing the reason associated with the SIP code.
+     * @param operationToken The token provided by the framework when
+     *         {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called.
+     *
+     */
+    public void respondToCapabilityRequestWithError(@NonNull Uri contactUri,
+            @SipResponseCode int code, @NonNull String reason, int operationToken) {
+        // Stub - to be implemented by service
+        Log.w(LOG_TAG, "respondToCapabiltyRequestWithError called with no implementation.");
+        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+    }
+}
diff --git a/test-base/Android.mk b/test-base/Android.mk
deleted file mode 100644
index a9d30cf..0000000
--- a/test-base/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-ifeq ($(HOST_OS),linux)
-# Build the legacy-performance-test-hostdex library
-# =================================================
-# This contains the android.test.PerformanceTestCase class only
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := src/android/test/PerformanceTestCase.java
-LOCAL_MODULE := legacy-performance-test-hostdex
-
-include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY)
-endif  # HOST_OS == linux
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 11150dd..b725920 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -386,6 +386,9 @@
   manifest_action["package-verifier"];
   manifest_action["meta-data"] = meta_data_action;
   manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);
+  manifest_action["queries"]["package"].Action(RequiredNameIsJavaPackage);
+  manifest_action["queries"]["intent"] = intent_filter_action;
+  // TODO: more complicated component name tag
 
   manifest_action["key-sets"]["key-set"]["public-key"];
   manifest_action["key-sets"]["upgrade-key-set"];
diff --git a/tools/dump-coverage/README.md b/tools/dump-coverage/README.md
index 2bab4bc..d1c10bc 100644
--- a/tools/dump-coverage/README.md
+++ b/tools/dump-coverage/README.md
@@ -16,7 +16,7 @@
 Then we can run the command to dump the data:
 
 ```
-adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so=dump:/data/data/com.android.deskclock/folder-to-use'
+adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so=dump:/data/data/com.android.deskclock/folder-to-use/coverage-file.ec'
 ```
 
 We can also reset the coverage information with
@@ -28,10 +28,10 @@
 then perform more actions, then dump the data again. To get the files, we can get
 
 ```
-adb pull /data/data/com.android.deskclock/folder-to-use ~/path-on-your-computer
+adb pull /data/data/com.android.deskclock/folder-to-use/coverage-file.ec ~/path-on-your-computer
 ```
 
-And you should have timestamped `.exec` files on your machine under the folder `~/path-on-your-computer`
+And you should have `coverage-file.ec` on your machine under the folder `~/path-on-your-computer`
 
 # Details
 
diff --git a/tools/dump-coverage/dump_coverage.cc b/tools/dump-coverage/dump_coverage.cc
index 3de1865..0808e77 100644
--- a/tools/dump-coverage/dump_coverage.cc
+++ b/tools/dump-coverage/dump_coverage.cc
@@ -18,20 +18,10 @@
 #include <jvmti.h>
 #include <string.h>
 
-#include <atomic>
-#include <ctime>
 #include <fstream>
-#include <iomanip>
-#include <iostream>
-#include <istream>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <vector>
 
 using std::get;
 using std::tuple;
-using std::chrono::system_clock;
 
 namespace dump_coverage {
 
@@ -87,35 +77,11 @@
   return java_result_array;
 }
 
-// Gets the filename to write execution data to
-//  dirname: the directory in which to place the file
-//  outputs <dirname>/YYYY-MM-DD-HH-MM-SS.SSS.exec
-static std::string GetFilename(const std::string& dirname) {
-  system_clock::time_point time_point = system_clock::now();
-  auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(time_point);
-  auto fractional_time = time_point - seconds;
-  auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(fractional_time);
-
-  std::time_t time = system_clock::to_time_t(time_point);
-  auto tm = *std::gmtime(&time);
-
-  std::ostringstream oss;
-  oss
-    << dirname
-    << "/"
-    << std::put_time(&tm, "%Y-%m-%d-%H-%M-%S.")
-    << std::setfill('0') << std::setw(3) << millis.count()
-    << ".ec";
-  return oss.str();
-}
-
-// Writes the execution data to a file
-//  data, length: represent the data, as a sequence of bytes
-//  dirname: directory name to contain the file
+// Writes the execution data to a file.
+//  data, length: represent the data, as a sequence of bytes.
+//  filename: file to write coverage data to.
 //  returns JNI_ERR if there is an error in writing the file, otherwise JNI_OK.
-static jint WriteFile(const char* data, int length, const std::string& dirname) {
-  auto filename = GetFilename(dirname);
-
+static jint WriteFile(const char* data, int length, const std::string& filename) {
   LOG(INFO) << "Writing file of length " << length << " to '" << filename
             << "'";
   std::ofstream file(filename, std::ios::binary);
@@ -136,11 +102,11 @@
   return JNI_OK;
 }
 
-// Grabs execution data and writes it to a file
-//  dirname: directory name to contain the file
+// Grabs execution data and writes it to a file.
+//  filename: file to write coverage data to.
 //  returns JNI_ERR if there is an error writing the file.
 // Will crash if the Agent isn't found or if any Java Exception occurs.
-static jint Dump(const std::string& dirname) {
+static jint Dump(const std::string& filename) {
   LOG(INFO) << "Dumping file";
 
   JNIEnv* env = GetJNIEnv();
@@ -152,12 +118,12 @@
 
   int result_len = env->GetArrayLength(java_result_array);
 
-  return WriteFile((const char*) result_ptr, result_len, dirname);
+  return WriteFile((const char*) result_ptr, result_len, filename);
 }
 
 // Resets execution data, performing the equivalent of
 //  Agent.getInstance().reset();
-//  args: should be empty
+//  args: should be empty.
 //  returns JNI_ERR if the arguments are invalid.
 // Will crash if the Agent isn't found or if any Java Exception occurs.
 static jint Reset(const std::string& args) {
diff --git a/tools/preload2/Android.bp b/tools/preload2/Android.bp
deleted file mode 100644
index 5809421..0000000
--- a/tools/preload2/Android.bp
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2015 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.
-
-java_library_host {
-    name: "preload2",
-
-    srcs: ["src/**/*.java"],
-
-    // To connect to devices (and take hprof dumps).
-    static_libs: [
-        "ddmlib-prebuilt",
-        "tools-common-prebuilt",
-
-        // To process hprof dumps.
-        "perflib-prebuilt",
-
-        "trove-prebuilt",
-        "guavalib",
-
-        // For JDWP access we use the framework in the JDWP tests from Apache Harmony, for
-        // convenience (and to not depend on internal JDK APIs).
-        "apache-harmony-jdwp-tests",
-        "junit",
-    ],
-
-    // Copy to build artifacts
-    dist: {
-        targets: [
-            "dist_files",
-        ],
-    },
-}
-
-// Copy the preload-tool shell script to the host's bin directory.
-sh_binary_host {
-    name: "preload-tool",
-    src: "preload-tool",
-    required: ["preload2"],
-}
diff --git a/tools/preload2/preload-tool b/tools/preload2/preload-tool
deleted file mode 100644
index 322b62f..0000000
--- a/tools/preload2/preload-tool
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (C) 2015 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.
-
-# This script is used on the host only. It uses a common subset
-# shell dialect that should work well. It is partially derived
-# from art/tools/art.
-
-function follow_links() {
-  if [ z"$BASH_SOURCE" != z ]; then
-    file="$BASH_SOURCE"
-  else
-    file="$0"
-  fi
-  while [ -h "$file" ]; do
-    # On Mac OS, readlink -f doesn't work.
-    file="$(readlink "$file")"
-  done
-  echo "$file"
-}
-
-
-PROG_NAME="$(follow_links)"
-PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
-ANDROID_ROOT=$PROG_DIR/..
-
-java -cp $ANDROID_ROOT/framework/preload2.jar com.android.preload.Main $@
diff --git a/tools/preload2/src/com/android/preload/ClientUtils.java b/tools/preload2/src/com/android/preload/ClientUtils.java
deleted file mode 100644
index 71ef025..0000000
--- a/tools/preload2/src/com/android/preload/ClientUtils.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload;
-
-import com.android.ddmlib.AndroidDebugBridge;
-import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-
-/**
- * Helper class for common communication with a Client (the ddms name for a running application).
- *
- * Instances take a default timeout parameter that's applied to all functions without explicit
- * timeout. Timeouts are in milliseconds.
- */
-public class ClientUtils {
-
-    private int defaultTimeout;
-
-    public ClientUtils() {
-        this(10000);
-    }
-
-    public ClientUtils(int defaultTimeout) {
-        this.defaultTimeout = defaultTimeout;
-    }
-
-    /**
-     * Shortcut for findClient with default timeout.
-     */
-    public Client findClient(IDevice device, String processName, int processPid) {
-        return findClient(device, processName, processPid, defaultTimeout);
-    }
-
-    /**
-     * Find the client with the given process name or process id. The name takes precedence over
-     * the process id (if valid). Stop looking after the given timeout.
-     *
-     * @param device The device to communicate with.
-     * @param processName The name of the process. May be null.
-     * @param processPid The pid of the process. Values less than or equal to zero are ignored.
-     * @param timeout The amount of milliseconds to wait, at most.
-     * @return The client, if found. Otherwise null.
-     */
-    public Client findClient(IDevice device, String processName, int processPid, int timeout) {
-        WaitForClient wfc = new WaitForClient(device, processName, processPid, timeout);
-        return wfc.get();
-    }
-
-    /**
-     * Shortcut for findAllClients with default timeout.
-     */
-    public Client[] findAllClients(IDevice device) {
-        return findAllClients(device, defaultTimeout);
-    }
-
-    /**
-     * Retrieve all clients known to the given device. Wait at most the given timeout.
-     *
-     * @param device The device to investigate.
-     * @param timeout The amount of milliseconds to wait, at most.
-     * @return An array of clients running on the given device. May be null depending on the
-     *         device implementation.
-     */
-    public Client[] findAllClients(IDevice device, int timeout) {
-        if (device.hasClients()) {
-            return device.getClients();
-        }
-        WaitForClients wfc = new WaitForClients(device, timeout);
-        return wfc.get();
-    }
-
-    private static class WaitForClient implements IClientChangeListener {
-
-        private IDevice device;
-        private String processName;
-        private int processPid;
-        private long timeout;
-        private Client result;
-
-        public WaitForClient(IDevice device, String processName, int processPid, long timeout) {
-            this.device = device;
-            this.processName = processName;
-            this.processPid = processPid;
-            this.timeout = timeout;
-            this.result = null;
-        }
-
-        public Client get() {
-            synchronized (this) {
-                AndroidDebugBridge.addClientChangeListener(this);
-
-                // Maybe it's already there.
-                if (result == null) {
-                    result = searchForClient(device);
-                }
-
-                if (result == null) {
-                    try {
-                        wait(timeout);
-                    } catch (InterruptedException e) {
-                        // Note: doesn't guard for spurious wakeup.
-                    }
-                }
-            }
-
-            AndroidDebugBridge.removeClientChangeListener(this);
-            return result;
-        }
-
-        private Client searchForClient(IDevice device) {
-            if (processName != null) {
-                Client tmp = device.getClient(processName);
-                if (tmp != null) {
-                    return tmp;
-                }
-            }
-            if (processPid > 0) {
-                String name = device.getClientName(processPid);
-                if (name != null && !name.isEmpty()) {
-                    Client tmp = device.getClient(name);
-                    if (tmp != null) {
-                        return tmp;
-                    }
-                }
-            }
-            if (processPid > 0) {
-                // Try manual search.
-                for (Client cl : device.getClients()) {
-                    if (cl.getClientData().getPid() == processPid
-                            && cl.getClientData().getClientDescription() != null) {
-                        return cl;
-                    }
-                }
-            }
-            return null;
-        }
-
-        private boolean isTargetClient(Client c) {
-            if (processPid > 0 && c.getClientData().getPid() == processPid) {
-                return true;
-            }
-            if (processName != null
-                    && processName.equals(c.getClientData().getClientDescription())) {
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public void clientChanged(Client arg0, int arg1) {
-            synchronized (this) {
-                if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
-                    if (isTargetClient(arg0)) {
-                        result = arg0;
-                        notifyAll();
-                    }
-                }
-            }
-        }
-    }
-
-    private static class WaitForClients implements IClientChangeListener {
-
-        private IDevice device;
-        private long timeout;
-
-        public WaitForClients(IDevice device, long timeout) {
-            this.device = device;
-            this.timeout = timeout;
-        }
-
-        public Client[] get() {
-            synchronized (this) {
-                AndroidDebugBridge.addClientChangeListener(this);
-
-                if (device.hasClients()) {
-                    return device.getClients();
-                }
-
-                try {
-                    wait(timeout); // Note: doesn't guard for spurious wakeup.
-                } catch (InterruptedException exc) {
-                }
-
-                // We will be woken up when the first client data arrives. Sleep a little longer
-                // to give (hopefully all of) the rest of the clients a chance to become available.
-                // Note: a loop with timeout is brittle as well and complicated, just accept this
-                //       for now.
-                try {
-                    Thread.sleep(500);
-                } catch (InterruptedException exc) {
-                }
-            }
-
-            AndroidDebugBridge.removeClientChangeListener(this);
-
-            return device.getClients();
-        }
-
-        @Override
-        public void clientChanged(Client arg0, int arg1) {
-            synchronized (this) {
-                if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) {
-                    notifyAll();
-                }
-            }
-        }
-    }
-}
diff --git a/tools/preload2/src/com/android/preload/DeviceUtils.java b/tools/preload2/src/com/android/preload/DeviceUtils.java
deleted file mode 100644
index 18cab7b..0000000
--- a/tools/preload2/src/com/android/preload/DeviceUtils.java
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload;
-
-import com.android.ddmlib.AdbCommandRejectedException;
-import com.android.ddmlib.AndroidDebugBridge;
-import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
-import com.android.preload.classdataretrieval.hprof.Hprof;
-import com.android.ddmlib.DdmPreferences;
-import com.android.ddmlib.IDevice;
-import com.android.ddmlib.IShellOutputReceiver;
-import com.android.ddmlib.SyncException;
-import com.android.ddmlib.TimeoutException;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Date;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Helper class for some device routines.
- */
-public class DeviceUtils {
-
-  // Locations
-  private static final String PRELOADED_CLASSES_FILE = "/etc/preloaded-classes";
-  // Shell commands
-  private static final String CREATE_EMPTY_PRELOADED_CMD = "touch " + PRELOADED_CLASSES_FILE;
-  private static final String DELETE_CACHE_CMD = "rm /data/dalvik-cache/*/*boot.art";
-  private static final String DELETE_PRELOADED_CMD = "rm " + PRELOADED_CLASSES_FILE;
-  private static final String READ_PRELOADED_CMD = "cat " + PRELOADED_CLASSES_FILE;
-  private static final String START_SHELL_CMD = "start";
-  private static final String STOP_SHELL_CMD = "stop";
-  private static final String REMOUNT_SYSTEM_CMD = "mount -o rw,remount /system";
-  private static final String UNSET_BOOTCOMPLETE_CMD = "setprop dev.bootcomplete \"0\"";
-
-  public static void init(int debugPort) {
-    DdmPreferences.setSelectedDebugPort(debugPort);
-
-    Hprof.init();
-
-    AndroidDebugBridge.init(true);
-
-    AndroidDebugBridge.createBridge();
-  }
-
-  /**
-   * Run a command in the shell on the device.
-   */
-  public static void doShell(IDevice device, String cmdline, long timeout, TimeUnit unit) {
-    doShell(device, cmdline, new NullShellOutputReceiver(), timeout, unit);
-  }
-
-  /**
-   * Run a command in the shell on the device. Collects and returns the console output.
-   */
-  public static String doShellReturnString(IDevice device, String cmdline, long timeout,
-      TimeUnit unit) {
-    CollectStringShellOutputReceiver rec = new CollectStringShellOutputReceiver();
-    doShell(device, cmdline, rec, timeout, unit);
-    return rec.toString();
-  }
-
-  /**
-   * Run a command in the shell on the device, directing all output to the given receiver.
-   */
-  public static void doShell(IDevice device, String cmdline, IShellOutputReceiver receiver,
-      long timeout, TimeUnit unit) {
-    try {
-      device.executeShellCommand(cmdline, receiver, timeout, unit);
-    } catch (Exception e) {
-      e.printStackTrace();
-    }
-  }
-
-  /**
-   * Run am start on the device.
-   */
-  public static void doAMStart(IDevice device, String name, String activity) {
-    doShell(device, "am start -n " + name + " /." + activity, 30, TimeUnit.SECONDS);
-  }
-
-  /**
-   * Find the device with the given serial. Give up after the given timeout (in milliseconds).
-   */
-  public static IDevice findDevice(String serial, int timeout) {
-    WaitForDevice wfd = new WaitForDevice(serial, timeout);
-    return wfd.get();
-  }
-
-  /**
-   * Get all devices ddms knows about. Wait at most for the given timeout.
-   */
-  public static IDevice[] findDevices(int timeout) {
-    WaitForDevice wfd = new WaitForDevice(null, timeout);
-    wfd.get();
-    return AndroidDebugBridge.getBridge().getDevices();
-  }
-
-  /**
-   * Return the build type of the given device. This is the value of the "ro.build.type"
-   * system property.
-   */
-  public static String getBuildType(IDevice device) {
-    try {
-      Future<String> buildType = device.getSystemProperty("ro.build.type");
-      return buildType.get(500, TimeUnit.MILLISECONDS);
-    } catch (Exception e) {
-    }
-    return null;
-  }
-
-  /**
-   * Check whether the given device has a pre-optimized boot image. More precisely, checks
-   * whether /system/framework/ * /boot.art exists.
-   */
-  public static boolean hasPrebuiltBootImage(IDevice device) {
-    String ret =
-        doShellReturnString(device, "ls /system/framework/*/boot.art", 500, TimeUnit.MILLISECONDS);
-
-    return !ret.contains("No such file or directory");
-  }
-
-    /**
-     * Write over the preloaded-classes file with an empty or existing file and regenerate the boot
-     * image as necessary.
-     *
-     * @param device
-     * @param pcFile
-     * @param bootTimeout
-     * @throws AdbCommandRejectedException
-     * @throws IOException
-     * @throws TimeoutException
-     * @throws SyncException
-     * @return true if successfully overwritten, false otherwise
-     */
-    public static boolean overwritePreloaded(IDevice device, File pcFile, long bootTimeout)
-            throws AdbCommandRejectedException, IOException, TimeoutException, SyncException {
-        boolean writeEmpty = (pcFile == null);
-        if (writeEmpty) {
-            // Check if the preloaded-classes file is already empty.
-            String oldContent =
-                    doShellReturnString(device, READ_PRELOADED_CMD, 1, TimeUnit.SECONDS);
-            if (oldContent.trim().equals("")) {
-                System.out.println("Preloaded-classes already empty.");
-                return true;
-            }
-        }
-
-        // Stop the system server etc.
-        doShell(device, STOP_SHELL_CMD, 1, TimeUnit.SECONDS);
-        // Remount the read-only system partition
-        doShell(device, REMOUNT_SYSTEM_CMD, 1, TimeUnit.SECONDS);
-        // Delete the preloaded-classes file
-        doShell(device, DELETE_PRELOADED_CMD, 1, TimeUnit.SECONDS);
-        // Delete the dalvik cache files
-        doShell(device, DELETE_CACHE_CMD, 1, TimeUnit.SECONDS);
-        if (writeEmpty) {
-            // Write an empty preloaded-classes file
-            doShell(device, CREATE_EMPTY_PRELOADED_CMD, 500, TimeUnit.MILLISECONDS);
-        } else {
-            // Push the new preloaded-classes file
-            device.pushFile(pcFile.getAbsolutePath(), PRELOADED_CLASSES_FILE);
-        }
-        // Manually reset the boot complete flag
-        doShell(device, UNSET_BOOTCOMPLETE_CMD, 1, TimeUnit.SECONDS);
-        // Restart system server on the device
-        doShell(device, START_SHELL_CMD, 1, TimeUnit.SECONDS);
-        // Wait for the boot complete flag and return the outcome.
-        return waitForBootComplete(device, bootTimeout);
-  }
-
-  private static boolean waitForBootComplete(IDevice device, long timeout) {
-    // Do a loop checking each second whether bootcomplete. Wait for at most the given
-    // threshold.
-    Date startDate = new Date();
-    for (;;) {
-      try {
-        Thread.sleep(1000);
-      } catch (InterruptedException e) {
-        // Ignore spurious wakeup.
-      }
-      // Check whether bootcomplete.
-      String ret =
-          doShellReturnString(device, "getprop dev.bootcomplete", 500, TimeUnit.MILLISECONDS);
-      if (ret.trim().equals("1")) {
-        break;
-      }
-      System.out.println("Still not booted: " + ret);
-
-      // Check whether we timed out. This is a simplistic check that doesn't take into account
-      // things like switches in time.
-      Date endDate = new Date();
-      long seconds =
-          TimeUnit.SECONDS.convert(endDate.getTime() - startDate.getTime(), TimeUnit.MILLISECONDS);
-      if (seconds > timeout) {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-  /**
-   * Enable method-tracing on device. The system should be restarted after this.
-   */
-  public static void enableTracing(IDevice device) {
-    // Disable selinux.
-    doShell(device, "setenforce 0", 100, TimeUnit.MILLISECONDS);
-
-    // Make the profile directory world-writable.
-    doShell(device, "chmod 777 /data/dalvik-cache/profiles", 100, TimeUnit.MILLISECONDS);
-
-    // Enable streaming method tracing with a small 1K buffer.
-    doShell(device, "setprop dalvik.vm.method-trace true", 100, TimeUnit.MILLISECONDS);
-    doShell(device, "setprop dalvik.vm.method-trace-file "
-                    + "/data/dalvik-cache/profiles/zygote.trace.bin", 100, TimeUnit.MILLISECONDS);
-    doShell(device, "setprop dalvik.vm.method-trace-file-siz 1024", 100, TimeUnit.MILLISECONDS);
-    doShell(device, "setprop dalvik.vm.method-trace-stream true", 100, TimeUnit.MILLISECONDS);
-  }
-
-  private static class NullShellOutputReceiver implements IShellOutputReceiver {
-    @Override
-    public boolean isCancelled() {
-      return false;
-    }
-
-    @Override
-    public void flush() {}
-
-    @Override
-    public void addOutput(byte[] arg0, int arg1, int arg2) {}
-  }
-
-  private static class CollectStringShellOutputReceiver implements IShellOutputReceiver {
-
-    private StringBuilder builder = new StringBuilder();
-
-    @Override
-    public String toString() {
-      String ret = builder.toString();
-      // Strip trailing newlines. They are especially ugly because adb uses DOS line endings.
-      while (ret.endsWith("\r") || ret.endsWith("\n")) {
-        ret = ret.substring(0, ret.length() - 1);
-      }
-      return ret;
-    }
-
-    @Override
-    public void addOutput(byte[] arg0, int arg1, int arg2) {
-      builder.append(new String(arg0, arg1, arg2));
-    }
-
-    @Override
-    public void flush() {}
-
-    @Override
-    public boolean isCancelled() {
-      return false;
-    }
-  }
-
-  private static class WaitForDevice {
-
-    private String serial;
-    private long timeout;
-    private IDevice device;
-
-    public WaitForDevice(String serial, long timeout) {
-      this.serial = serial;
-      this.timeout = timeout;
-      device = null;
-    }
-
-    public IDevice get() {
-      if (device == null) {
-          WaitForDeviceListener wfdl = new WaitForDeviceListener(serial);
-          synchronized (wfdl) {
-              AndroidDebugBridge.addDeviceChangeListener(wfdl);
-
-              // Check whether we already know about this device.
-              IDevice[] devices = AndroidDebugBridge.getBridge().getDevices();
-              if (serial != null) {
-                  for (IDevice d : devices) {
-                      if (serial.equals(d.getSerialNumber())) {
-                          // Only accept if there are clients already. Else wait for the callback informing
-                          // us that we now have clients.
-                          if (d.hasClients()) {
-                              device = d;
-                          }
-
-                          break;
-                      }
-                  }
-              } else {
-                  if (devices.length > 0) {
-                      device = devices[0];
-                  }
-              }
-
-              if (device == null) {
-                  try {
-                      wfdl.wait(timeout);
-                  } catch (InterruptedException e) {
-                      // Ignore spurious wakeups.
-                  }
-                  device = wfdl.getDevice();
-              }
-
-              AndroidDebugBridge.removeDeviceChangeListener(wfdl);
-          }
-      }
-
-      if (device != null) {
-          // Wait for clients.
-          WaitForClientsListener wfcl = new WaitForClientsListener(device);
-          synchronized (wfcl) {
-              AndroidDebugBridge.addDeviceChangeListener(wfcl);
-
-              if (!device.hasClients()) {
-                  try {
-                      wfcl.wait(timeout);
-                  } catch (InterruptedException e) {
-                      // Ignore spurious wakeups.
-                  }
-              }
-
-              AndroidDebugBridge.removeDeviceChangeListener(wfcl);
-          }
-      }
-
-      return device;
-    }
-
-    private static class WaitForDeviceListener implements IDeviceChangeListener {
-
-        private String serial;
-        private IDevice device;
-
-        public WaitForDeviceListener(String serial) {
-            this.serial = serial;
-        }
-
-        public IDevice getDevice() {
-            return device;
-        }
-
-        @Override
-        public void deviceChanged(IDevice arg0, int arg1) {
-            // We may get a device changed instead of connected. Handle like a connection.
-            deviceConnected(arg0);
-        }
-
-        @Override
-        public void deviceConnected(IDevice arg0) {
-            if (device != null) {
-                // Ignore updates.
-                return;
-            }
-
-            if (serial == null || serial.equals(arg0.getSerialNumber())) {
-                device = arg0;
-                synchronized (this) {
-                    notifyAll();
-                }
-            }
-        }
-
-        @Override
-        public void deviceDisconnected(IDevice arg0) {
-            // Ignore disconnects.
-        }
-
-    }
-
-    private static class WaitForClientsListener implements IDeviceChangeListener {
-
-        private IDevice myDevice;
-
-        public WaitForClientsListener(IDevice myDevice) {
-            this.myDevice = myDevice;
-        }
-
-        @Override
-        public void deviceChanged(IDevice arg0, int arg1) {
-            if (arg0 == myDevice && (arg1 & IDevice.CHANGE_CLIENT_LIST) != 0) {
-                // Got a client list, done here.
-                synchronized (this) {
-                    notifyAll();
-                }
-            }
-        }
-
-        @Override
-        public void deviceConnected(IDevice arg0) {
-        }
-
-        @Override
-        public void deviceDisconnected(IDevice arg0) {
-        }
-
-    }
-  }
-
-}
diff --git a/tools/preload2/src/com/android/preload/DumpData.java b/tools/preload2/src/com/android/preload/DumpData.java
deleted file mode 100644
index d997224..0000000
--- a/tools/preload2/src/com/android/preload/DumpData.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Holds the collected data for a process.
- */
-public class DumpData {
-    /**
-     * Name of the package (=application).
-     */
-    String packageName;
-
-    /**
-     * A map of class name to a string for the classloader. This may be a toString equivalent,
-     * or just a unique ID.
-     */
-    Map<String, String> dumpData;
-
-    /**
-     * The Date when this data was captured. Mostly for display purposes.
-     */
-    Date date;
-
-    /**
-     * A cached value for the number of boot classpath classes (classloader value in dumpData is
-     * null).
-     */
-    int bcpClasses;
-
-    public DumpData(String packageName, Map<String, String> dumpData, Date date) {
-        this.packageName = packageName;
-        this.dumpData = dumpData;
-        this.date = date;
-
-        countBootClassPath();
-    }
-
-    public String getPackageName() {
-        return packageName;
-    }
-
-    public Date getDate() {
-        return date;
-    }
-
-    public Map<String, String> getDumpData() {
-        return dumpData;
-    }
-
-    public void countBootClassPath() {
-        bcpClasses = 0;
-        for (Map.Entry<String, String> e : dumpData.entrySet()) {
-            if (e.getValue() == null) {
-                bcpClasses++;
-            }
-        }
-    }
-
-    // Return an inverted mapping.
-    public Map<String, Set<String>> invertData() {
-        Map<String, Set<String>> ret = new HashMap<>();
-        for (Map.Entry<String, String> e : dumpData.entrySet()) {
-            if (!ret.containsKey(e.getValue())) {
-                ret.put(e.getValue(), new HashSet<String>());
-            }
-            ret.get(e.getValue()).add(e.getKey());
-        }
-        return ret;
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/DumpDataIO.java b/tools/preload2/src/com/android/preload/DumpDataIO.java
deleted file mode 100644
index 28625c5..0000000
--- a/tools/preload2/src/com/android/preload/DumpDataIO.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.DefaultHandler;
-
-import java.io.File;
-import java.io.FileReader;
-import java.text.DateFormat;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Map;
-
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-/**
- * Helper class for serialization and deserialization of a collection of DumpData objects to XML.
- */
-public class DumpDataIO {
-
-  /**
-   * Serialize the given collection to an XML document. Returns the produced string.
-   */
-  public static String serialize(Collection<DumpData> data) {
-      // We'll do this by hand, constructing a DOM or similar is too complicated for our simple
-      // use case.
-
-      StringBuilder sb = new StringBuilder();
-      sb.append("<preloaded-classes-data>\n");
-
-      for (DumpData d : data) {
-          serialize(d, sb);
-      }
-
-      sb.append("</preloaded-classes-data>\n");
-      return sb.toString();
-  }
-
-  private static void serialize(DumpData d, StringBuilder sb) {
-      sb.append("<data package=\"" + d.packageName + "\" date=\"" +
-              DateFormat.getDateTimeInstance().format(d.date) +"\">\n");
-
-      for (Map.Entry<String, String> e : d.dumpData.entrySet()) {
-          sb.append("<class name=\"" + e.getKey() + "\" classloader=\"" + e.getValue() + "\"/>\n");
-      }
-
-      sb.append("</data>\n");
-  }
-
-  /**
-   * Load a collection of DumpData objects from the given file.
-   */
-  public static Collection<DumpData> deserialize(File f) throws Exception {
-      // Use SAX parsing. Our format is very simple. Don't do any schema validation or such.
-
-      SAXParserFactory spf = SAXParserFactory.newInstance();
-      spf.setNamespaceAware(false);
-      SAXParser saxParser = spf.newSAXParser();
-
-      XMLReader xmlReader = saxParser.getXMLReader();
-      DumpDataContentHandler ddch = new DumpDataContentHandler();
-      xmlReader.setContentHandler(ddch);
-      xmlReader.parse(new InputSource(new FileReader(f)));
-
-      return ddch.data;
-  }
-
-  private static class DumpDataContentHandler extends DefaultHandler {
-      Collection<DumpData> data = new LinkedList<DumpData>();
-      DumpData openData = null;
-
-      @Override
-      public void startElement(String uri, String localName, String qName, Attributes attributes)
-              throws SAXException {
-          if (qName.equals("data")) {
-              if (openData != null) {
-                  throw new IllegalStateException();
-              }
-              String pkg = attributes.getValue("package");
-              String dateString = attributes.getValue("date");
-
-              if (pkg == null || dateString == null) {
-                  throw new IllegalArgumentException();
-              }
-
-              try {
-                  Date date = DateFormat.getDateTimeInstance().parse(dateString);
-                  openData = new DumpData(pkg, new HashMap<String, String>(), date);
-              } catch (Exception e) {
-                  throw new RuntimeException(e);
-              }
-          } else if (qName.equals("class")) {
-              if (openData == null) {
-                  throw new IllegalStateException();
-              }
-              String className = attributes.getValue("name");
-              String classLoader = attributes.getValue("classloader");
-
-              if (className == null || classLoader == null) {
-                  throw new IllegalArgumentException();
-              }
-
-              openData.dumpData.put(className, classLoader.equals("null") ? null : classLoader);
-          }
-      }
-
-      @Override
-      public void endElement(String uri, String localName, String qName) throws SAXException {
-          if (qName.equals("data")) {
-              if (openData == null) {
-                  throw new IllegalStateException();
-              }
-              openData.countBootClassPath();
-
-              data.add(openData);
-              openData = null;
-          }
-      }
-  }
-}
diff --git a/tools/preload2/src/com/android/preload/DumpTableModel.java b/tools/preload2/src/com/android/preload/DumpTableModel.java
deleted file mode 100644
index d97cbf0..0000000
--- a/tools/preload2/src/com/android/preload/DumpTableModel.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.table.AbstractTableModel;
-
-/**
- * A table model for collected DumpData. This is both the internal storage as well as the model
- * for display.
- */
-public class DumpTableModel extends AbstractTableModel {
-
-    private List<DumpData> data = new ArrayList<DumpData>();
-
-    public void addData(DumpData d) {
-        data.add(d);
-        fireTableRowsInserted(data.size() - 1, data.size() - 1);
-    }
-
-    public void clear() {
-        int size = data.size();
-        if (size > 0) {
-            data.clear();
-            fireTableRowsDeleted(0, size - 1);
-        }
-    }
-
-    public List<DumpData> getData() {
-        return data;
-    }
-
-    @Override
-    public int getRowCount() {
-        return data.size();
-    }
-
-    @Override
-    public int getColumnCount() {
-        return 4;
-    }
-
-    @Override
-    public String getColumnName(int column) {
-        switch (column) {
-            case 0:
-                return "Package";
-            case 1:
-                return "Date";
-            case 2:
-                return "# All Classes";
-            case 3:
-                return "# Boot Classpath Classes";
-
-            default:
-                throw new IndexOutOfBoundsException(String.valueOf(column));
-        }
-    }
-
-    @Override
-    public Object getValueAt(int rowIndex, int columnIndex) {
-        DumpData d = data.get(rowIndex);
-        switch (columnIndex) {
-            case 0:
-                return d.packageName;
-            case 1:
-                return d.date;
-            case 2:
-                return d.dumpData.size();
-            case 3:
-                return d.bcpClasses;
-
-            default:
-                throw new IndexOutOfBoundsException(String.valueOf(columnIndex));
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/Main.java b/tools/preload2/src/com/android/preload/Main.java
deleted file mode 100644
index 2265e95..0000000
--- a/tools/preload2/src/com/android/preload/Main.java
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.actions.ClearTableAction;
-import com.android.preload.actions.ComputeThresholdAction;
-import com.android.preload.actions.ComputeThresholdXAction;
-import com.android.preload.actions.DeviceSpecific;
-import com.android.preload.actions.ExportAction;
-import com.android.preload.actions.ImportAction;
-import com.android.preload.actions.ReloadListAction;
-import com.android.preload.actions.RunMonkeyAction;
-import com.android.preload.actions.ScanAllPackagesAction;
-import com.android.preload.actions.ScanPackageAction;
-import com.android.preload.actions.ShowDataAction;
-import com.android.preload.actions.WritePreloadedClassesAction;
-import com.android.preload.classdataretrieval.ClassDataRetriever;
-import com.android.preload.classdataretrieval.hprof.Hprof;
-import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever;
-import com.android.preload.ui.IUI;
-import com.android.preload.ui.SequenceUI;
-import com.android.preload.ui.SwingUI;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
-
-import javax.swing.Action;
-import javax.swing.DefaultListModel;
-
-public class Main {
-
-    /**
-     * Enable tracing mode. This is a work-in-progress to derive compiled-methods data, so it is
-     * off for now.
-     */
-    public final static boolean ENABLE_TRACING = false;
-
-    /**
-     * Ten-second timeout.
-     */
-    public final static int DEFAULT_TIMEOUT_MILLIS = 10 * 1000;
-
-    /**
-     * Hprof timeout. Two minutes.
-     */
-    public final static int HPROF_TIMEOUT_MILLIS = 120 * 1000;
-
-    private IDevice device;
-    private static ClientUtils clientUtils;
-
-    private DumpTableModel dataTableModel;
-    private DefaultListModel<Client> clientListModel;
-
-    private IUI ui;
-
-    // Actions that need to be updated once a device is selected.
-    private Collection<DeviceSpecific> deviceSpecificActions;
-
-    // Current main instance.
-    private static Main top;
-    private static boolean useJdwpClassDataRetriever = false;
-
-    public final static String CLASS_PRELOAD_BLACKLIST = "android.app.AlarmManager$" + "|"
-            + "android.app.SearchManager$" + "|" + "android.os.FileObserver$" + "|"
-            + "com.android.server.PackageManagerService\\$AppDirObserver$" + "|" +
-
-
-            // Threads
-            "android.os.AsyncTask$" + "|" + "android.pim.ContactsAsyncHelper$" + "|"
-            + "android.webkit.WebViewClassic\\$1$" + "|" + "java.lang.ProcessManager$" + "|"
-            + "(.*\\$NoPreloadHolder$)";
-
-    public final static String SCAN_ALL_CMD = "scan-all";
-    public final static String SCAN_PACKAGE_CMD = "scan";
-    public final static String COMPUTE_FILE_CMD = "comp";
-    public final static String EXPORT_CMD = "export";
-    public final static String IMPORT_CMD = "import";
-    public final static String WRITE_CMD = "write";
-
-    /**
-     * @param args
-     */
-    public static void main(String[] args) {
-        Main m;
-        if (args.length > 0 && args[0].equals("--seq")) {
-            m = createSequencedMain(args);
-        } else {
-            m = new Main(new SwingUI());
-        }
-
-        top = m;
-        m.startUp();
-    }
-
-    public Main(IUI ui) {
-        this.ui = ui;
-
-        clientListModel = new DefaultListModel<Client>();
-        dataTableModel = new DumpTableModel();
-
-        clientUtils = new ClientUtils(DEFAULT_TIMEOUT_MILLIS);  // Client utils with 10s timeout.
-
-        List<Action> actions = new ArrayList<Action>();
-        actions.add(new ReloadListAction(clientUtils, null, clientListModel));
-        actions.add(new ClearTableAction(dataTableModel));
-        actions.add(new RunMonkeyAction(null, dataTableModel));
-        actions.add(new ScanPackageAction(clientUtils, null, dataTableModel));
-        actions.add(new ScanAllPackagesAction(clientUtils, null, dataTableModel));
-        actions.add(new ComputeThresholdAction("Compute preloaded-classes", dataTableModel, 2,
-                CLASS_PRELOAD_BLACKLIST));
-        actions.add(new ComputeThresholdAction("Compute compiled-classes", dataTableModel, 1,
-                null));
-        actions.add(new ComputeThresholdXAction("Compute(X)", dataTableModel,
-                CLASS_PRELOAD_BLACKLIST));
-        actions.add(new WritePreloadedClassesAction(clientUtils, null, dataTableModel));
-        actions.add(new ShowDataAction(dataTableModel));
-        actions.add(new ImportAction(dataTableModel));
-        actions.add(new ExportAction(dataTableModel));
-
-        deviceSpecificActions = new ArrayList<DeviceSpecific>();
-        for (Action a : actions) {
-            if (a instanceof DeviceSpecific) {
-                deviceSpecificActions.add((DeviceSpecific)a);
-            }
-        }
-
-        ui.prepare(clientListModel, dataTableModel, actions);
-    }
-
-    /**
-     * @param args
-     * @return
-     */
-    private static Main createSequencedMain(String[] args) {
-        SequenceUI ui = new SequenceUI();
-        Main main = new Main(ui);
-
-        Iterator<String> it = Arrays.asList(args).iterator();
-        it.next();  // --seq
-        // Setup
-        ui.choice("#" + it.next());  // Device.
-        ui.confirmNo();              // Prepare: no.
-        // Actions
-        try {
-            while (it.hasNext()) {
-                String op = it.next();
-                // Operation: Scan a single package
-                if (SCAN_PACKAGE_CMD.equals(op)) {
-                    System.out.println("Scanning package.");
-                    ui.action(ScanPackageAction.class);
-                    ui.client(it.next());
-                // Operation: Scan all packages
-                } else if (SCAN_ALL_CMD.equals(op)) {
-                    System.out.println("Scanning all packages.");
-                    ui.action(ScanAllPackagesAction.class);
-                // Operation: Export the output to a file
-                } else if (EXPORT_CMD.equals(op)) {
-                    System.out.println("Exporting data.");
-                    ui.action(ExportAction.class);
-                    ui.output(new File(it.next()));
-                // Operation: Import the input from a file or directory
-                } else if (IMPORT_CMD.equals(op)) {
-                    System.out.println("Importing data.");
-                    File file = new File(it.next());
-                    if (!file.exists()) {
-                        throw new RuntimeException(
-                                String.format("File does not exist, %s.", file.getAbsolutePath()));
-                    } else if (file.isFile()) {
-                        ui.action(ImportAction.class);
-                        ui.input(file);
-                    } else if (file.isDirectory()) {
-                        for (File content : file.listFiles()) {
-                            ui.action(ImportAction.class);
-                            ui.input(content);
-                        }
-                    }
-                // Operation: Compute preloaded classes with specific threshold
-                } else if (COMPUTE_FILE_CMD.equals(op)) {
-                    System.out.println("Compute preloaded classes.");
-                    ui.action(ComputeThresholdXAction.class);
-                    ui.input(it.next());
-                    ui.confirmYes();
-                    ui.output(new File(it.next()));
-                // Operation: Write preloaded classes from a specific file
-                } else if (WRITE_CMD.equals(op)) {
-                    System.out.println("Writing preloaded classes.");
-                    ui.action(WritePreloadedClassesAction.class);
-                    ui.input(new File(it.next()));
-                }
-            }
-        } catch (NoSuchElementException e) {
-            System.out.println("Failed to parse action sequence correctly.");
-            throw e;
-        }
-
-        return main;
-    }
-
-    public static IUI getUI() {
-        return top.ui;
-    }
-
-    public static ClassDataRetriever getClassDataRetriever() {
-        if (useJdwpClassDataRetriever) {
-            return new JDWPClassDataRetriever();
-        } else {
-            return new Hprof(HPROF_TIMEOUT_MILLIS);
-        }
-    }
-
-    public IDevice getDevice() {
-        return device;
-    }
-
-    public void setDevice(IDevice device) {
-        this.device = device;
-        for (DeviceSpecific ds : deviceSpecificActions) {
-            ds.setDevice(device);
-        }
-    }
-
-    public DefaultListModel<Client> getClientListModel() {
-        return clientListModel;
-    }
-
-    static class DeviceWrapper {
-        IDevice device;
-
-        public DeviceWrapper(IDevice d) {
-            device = d;
-        }
-
-        @Override
-        public String toString() {
-            return device.getName() + " (#" + device.getSerialNumber() + ")";
-        }
-    }
-
-    private void startUp() {
-        getUI().showWaitDialog();
-        initDevice();
-
-        // Load clients.
-        new ReloadListAction(clientUtils, getDevice(), clientListModel).run();
-
-        getUI().hideWaitDialog();
-        getUI().ready();
-    }
-
-    private void initDevice() {
-        DeviceUtils.init(DEFAULT_TIMEOUT_MILLIS);
-
-        IDevice devices[] = DeviceUtils.findDevices(DEFAULT_TIMEOUT_MILLIS);
-        if (devices == null || devices.length == 0) {
-            throw new RuntimeException("Could not find any devices...");
-        }
-
-        getUI().hideWaitDialog();
-
-        DeviceWrapper deviceWrappers[] = new DeviceWrapper[devices.length];
-        for (int i = 0; i < devices.length; i++) {
-            deviceWrappers[i] = new DeviceWrapper(devices[i]);
-        }
-
-        DeviceWrapper ret = Main.getUI().showChoiceDialog("Choose a device", "Choose device",
-                deviceWrappers);
-        if (ret != null) {
-            setDevice(ret.device);
-        } else {
-            System.exit(0);
-        }
-
-        boolean prepare = Main.getUI().showConfirmDialog("Prepare device?",
-                "Do you want to prepare the device? This is highly recommended.");
-        if (prepare) {
-            String buildType = DeviceUtils.getBuildType(device);
-            if (buildType == null || (!buildType.equals("userdebug") && !buildType.equals("eng"))) {
-                Main.getUI().showMessageDialog("Need a userdebug or eng build! (Found " + buildType
-                        + ")");
-                return;
-            }
-            if (DeviceUtils.hasPrebuiltBootImage(device)) {
-                Main.getUI().showMessageDialog("Cannot prepare a device with pre-optimized boot "
-                        + "image!");
-                return;
-            }
-
-            if (ENABLE_TRACING) {
-                DeviceUtils.enableTracing(device);
-            }
-
-            Main.getUI().showMessageDialog("The device will reboot. This will potentially take a "
-                    + "long time. Please be patient.");
-            boolean success = false;
-            try {
-                success = DeviceUtils.overwritePreloaded(device, null, 15 * 60);
-            } catch (Exception e) {
-                System.err.println(e);
-            } finally {
-                if (!success) {
-                    Main.getUI().showMessageDialog(
-                            "Removing preloaded-classes failed unexpectedly!");
-                }
-            }
-        }
-    }
-
-    public static Map<String, String> findAndGetClassData(IDevice device, String packageName)
-            throws Exception {
-        Client client = clientUtils.findClient(device, packageName, -1);
-        if (client == null) {
-            throw new RuntimeException("Could not find client...");
-        }
-        System.out.println("Found client: " + client);
-
-        return getClassDataRetriever().getClassData(client);
-    }
-
-}
diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
deleted file mode 100644
index 5787d85..0000000
--- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.preload.Main;
-import java.awt.event.ActionEvent;
-
-import javax.swing.AbstractAction;
-
-public abstract class AbstractThreadedAction extends AbstractAction implements Runnable {
-
-    protected AbstractThreadedAction(String title) {
-        super(title);
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        if (Main.getUI().isSingleThreaded()) {
-            run();
-        } else {
-            new Thread(this).start();
-        }
-    }
-
-}
diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java
deleted file mode 100644
index 7906417..0000000
--- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.ddmlib.IDevice;
-
-import java.awt.event.ActionEvent;
-
-public abstract class AbstractThreadedDeviceSpecificAction extends AbstractThreadedAction
-        implements DeviceSpecific {
-
-    protected IDevice device;
-
-    protected AbstractThreadedDeviceSpecificAction(String title, IDevice device) {
-        super(title);
-        this.device = device;
-    }
-
-    @Override
-    public void setDevice(IDevice device) {
-        this.device = device;
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        if (device == null) {
-            return;
-        }
-        super.actionPerformed(e);
-    }
-}
diff --git a/tools/preload2/src/com/android/preload/actions/ClearTableAction.java b/tools/preload2/src/com/android/preload/actions/ClearTableAction.java
deleted file mode 100644
index c0e4795..0000000
--- a/tools/preload2/src/com/android/preload/actions/ClearTableAction.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.preload.DumpTableModel;
-
-import java.awt.event.ActionEvent;
-
-import javax.swing.AbstractAction;
-
-public class ClearTableAction extends AbstractAction {
-    private final DumpTableModel dataTableModel;
-
-    public ClearTableAction(DumpTableModel dataTableModel) {
-        super("Clear");
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        dataTableModel.clear();
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
deleted file mode 100644
index 3a7f7f7..0000000
--- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.regex.Pattern;
-
-import javax.swing.AbstractAction;
-
-/**
- * Compute an intersection of classes from the given data. A class is in the intersection if it
- * appears in at least the number of threshold given packages. An optional blacklist can be
- * used to filter classes from the intersection.
- */
-public class ComputeThresholdAction extends AbstractThreadedAction {
-    protected int threshold;
-    private Pattern blacklist;
-    private DumpTableModel dataTableModel;
-
-    /**
-     * Create an action with the given parameters. The blacklist is a regular expression
-     * that filters classes.
-     */
-    public ComputeThresholdAction(String name, DumpTableModel dataTableModel, int threshold,
-            String blacklist) {
-        super(name);
-        this.dataTableModel = dataTableModel;
-        this.threshold = threshold;
-        if (blacklist != null) {
-            this.blacklist = Pattern.compile(blacklist);
-        }
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        List<DumpData> data = dataTableModel.getData();
-        if (data.size() == 0) {
-            Main.getUI().showMessageDialog("No data available, please scan packages or run "
-                    + "monkeys.");
-            return;
-        }
-        if (data.size() == 1) {
-            Main.getUI().showMessageDialog("Cannot compute list from only one data set, please "
-                    + "scan packages or run monkeys.");
-            return;
-        }
-
-        super.actionPerformed(e);
-    }
-
-    @Override
-    public void run() {
-        Main.getUI().showWaitDialog();
-
-        Map<String, Set<String>> uses = new HashMap<String, Set<String>>();
-        for (DumpData d : dataTableModel.getData()) {
-            Main.getUI().updateWaitDialog("Merging " + d.getPackageName());
-            updateClassUse(d.getPackageName(), uses, getBootClassPathClasses(d.getDumpData()));
-        }
-
-        Main.getUI().updateWaitDialog("Computing thresholded set");
-        Set<String> result = fromThreshold(uses, blacklist, threshold);
-        Main.getUI().hideWaitDialog();
-
-        boolean ret = Main.getUI().showConfirmDialog("Computed a set with " + result.size()
-                + " classes, would you like to save to disk?", "Save?");
-        if (ret) {
-            File f = Main.getUI().showSaveDialog();
-            if (f != null) {
-                saveSet(result, f);
-            }
-        }
-    }
-
-    private Set<String> fromThreshold(Map<String, Set<String>> classUses, Pattern blacklist,
-            int threshold) {
-        TreeSet<String> ret = new TreeSet<>(); // TreeSet so it's nicely ordered by name.
-
-        for (Map.Entry<String, Set<String>> e : classUses.entrySet()) {
-            if (e.getValue().size() >= threshold) {
-                if (blacklist == null || !blacklist.matcher(e.getKey()).matches()) {
-                    ret.add(e.getKey());
-                }
-            }
-        }
-
-        return ret;
-    }
-
-    private static void updateClassUse(String pkg, Map<String, Set<String>> classUses,
-            Set<String> classes) {
-        for (String className : classes) {
-            Set<String> old = classUses.get(className);
-            if (old == null) {
-                classUses.put(className, new HashSet<String>());
-            }
-            classUses.get(className).add(pkg);
-        }
-    }
-
-    private static Set<String> getBootClassPathClasses(Map<String, String> source) {
-        Set<String> ret = new HashSet<>();
-        for (Map.Entry<String, String> e : source.entrySet()) {
-            if (e.getValue() == null) {
-                ret.add(e.getKey());
-            }
-        }
-        return ret;
-    }
-
-    private static void saveSet(Set<String> result, File f) {
-        try {
-            PrintWriter out = new PrintWriter(f);
-            for (String s : result) {
-                out.println(s);
-            }
-            out.close();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java
deleted file mode 100644
index 3ec0a4c..0000000
--- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-public class ComputeThresholdXAction extends ComputeThresholdAction {
-
-    public ComputeThresholdXAction(String name, DumpTableModel dataTableModel,
-            String blacklist) {
-        super(name, dataTableModel, 1, blacklist);
-    }
-
-    @Override
-    public void run() {
-        String value = Main.getUI().showInputDialog("Threshold?");
-
-        if (value != null) {
-            try {
-                threshold = Integer.parseInt(value);
-                super.run();
-            } catch (Exception exc) {
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java b/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java
deleted file mode 100644
index 35a8f26..0000000
--- a/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.ddmlib.IDevice;
-
-/**
- * Marks an action as being device-specific. The user must set the device through the specified
- * method if the device selection changes.
- *
- * Implementors must tolerate a null device (for example, with a no-op). This includes calling
- * any methods before setDevice has been called.
- */
-public interface DeviceSpecific {
-
-    /**
-     * Set the device that should be used. Note that there is no restriction on calling other
-     * methods of the implementor before a setDevice call. Neither is device guaranteed to be
-     * non-null.
-     *
-     * @param device The device to use going forward.
-     */
-    public void setDevice(IDevice device);
-}
diff --git a/tools/preload2/src/com/android/preload/actions/ExportAction.java b/tools/preload2/src/com/android/preload/actions/ExportAction.java
deleted file mode 100644
index 848a568..0000000
--- a/tools/preload2/src/com/android/preload/actions/ExportAction.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.preload.DumpDataIO;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.io.PrintWriter;
-
-public class ExportAction extends AbstractThreadedAction {
-    private File lastSaveFile;
-    private DumpTableModel dataTableModel;
-
-    public ExportAction(DumpTableModel dataTableModel) {
-        super("Export data");
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        lastSaveFile = Main.getUI().showSaveDialog();
-        if (lastSaveFile != null) {
-            super.actionPerformed(e);
-        }
-    }
-
-    @Override
-    public void run() {
-        Main.getUI().showWaitDialog();
-
-        String serialized = DumpDataIO.serialize(dataTableModel.getData());
-
-        if (serialized != null) {
-            try {
-                PrintWriter out = new PrintWriter(lastSaveFile);
-                out.println(serialized);
-                out.close();
-
-                Main.getUI().hideWaitDialog();
-            } catch (Exception e) {
-                Main.getUI().hideWaitDialog();
-                Main.getUI().showMessageDialog("Failed writing: " + e.getMessage());
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ImportAction.java b/tools/preload2/src/com/android/preload/actions/ImportAction.java
deleted file mode 100644
index bfeeb83..0000000
--- a/tools/preload2/src/com/android/preload/actions/ImportAction.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.preload.DumpData;
-import com.android.preload.DumpDataIO;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.util.Collection;
-
-import javax.swing.AbstractAction;
-
-public class ImportAction extends AbstractThreadedAction {
-    private File[] lastOpenFiles;
-    private DumpTableModel dataTableModel;
-
-    public ImportAction(DumpTableModel dataTableModel) {
-        super("Import data");
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        lastOpenFiles = Main.getUI().showOpenDialog(true);
-        if (lastOpenFiles != null) {
-            super.actionPerformed(e);
-        }
-    }
-
-    @Override
-    public void run() {
-        Main.getUI().showWaitDialog();
-
-        try {
-            for (File f : lastOpenFiles) {
-                try {
-                    Collection<DumpData> data = DumpDataIO.deserialize(f);
-
-                    for (DumpData d : data) {
-                        dataTableModel.addData(d);
-                    }
-                } catch (Exception e) {
-                    Main.getUI().showMessageDialog("Failed reading: " + e.getMessage());
-                }
-            }
-        } finally {
-            Main.getUI().hideWaitDialog();
-        }
-
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ReloadListAction.java b/tools/preload2/src/com/android/preload/actions/ReloadListAction.java
deleted file mode 100644
index 29f0557..0000000
--- a/tools/preload2/src/com/android/preload/actions/ReloadListAction.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-
-import java.util.Arrays;
-import java.util.Comparator;
-
-import javax.swing.DefaultListModel;
-
-public class ReloadListAction extends AbstractThreadedDeviceSpecificAction {
-
-    private ClientUtils clientUtils;
-    private final DefaultListModel<Client> clientListModel;
-
-    public ReloadListAction(ClientUtils utils, IDevice device,
-            DefaultListModel<Client> clientListModel) {
-        super("Reload", device);
-        this.clientUtils = utils;
-        this.clientListModel = clientListModel;
-    }
-
-    @Override
-    public void run() {
-        Client[] clients = clientUtils.findAllClients(device);
-        if (clients != null) {
-            Arrays.sort(clients, new ClientComparator());
-        }
-        clientListModel.removeAllElements();
-        for (Client c : clients) {
-            clientListModel.addElement(c);
-        }
-    }
-
-    private static class ClientComparator implements Comparator<Client> {
-
-        @Override
-        public int compare(Client o1, Client o2) {
-            String s1 = o1.getClientData().getClientDescription();
-            String s2 = o2.getClientData().getClientDescription();
-
-            if (s1 == null || s2 == null) {
-                // Not good, didn't get all data?
-                return (s1 == null) ? -1 : 1;
-            }
-
-            return s1.compareTo(s2);
-        }
-
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java b/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
deleted file mode 100644
index 29464fc..0000000
--- a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.ddmlib.IDevice;
-import com.android.preload.DeviceUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.util.Date;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import javax.swing.AbstractAction;
-
-public class RunMonkeyAction extends AbstractAction implements DeviceSpecific {
-
-    private final static String DEFAULT_MONKEY_PACKAGES =
-            "com.android.calendar,com.android.gallery3d";
-
-    private IDevice device;
-    private DumpTableModel dataTableModel;
-
-    public RunMonkeyAction(IDevice device, DumpTableModel dataTableModel) {
-        super("Run monkey");
-        this.device = device;
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void setDevice(IDevice device) {
-        this.device = device;
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        String packages = Main.getUI().showInputDialog("Please enter packages name to run with"
-                + " the monkey, or leave empty for default.");
-        if (packages == null) {
-            return;
-        }
-        if (packages.isEmpty()) {
-            packages = DEFAULT_MONKEY_PACKAGES;
-        }
-        Runnable r = new RunMonkeyRunnable(packages);
-        if (Main.getUI().isSingleThreaded()) {
-            r.run();
-        } else {
-            new Thread(r).start();
-        }
-    }
-
-    private class RunMonkeyRunnable implements Runnable {
-
-        private String packages;
-        private final static int ITERATIONS = 1000;
-
-        public RunMonkeyRunnable(String packages) {
-            this.packages = packages;
-        }
-
-        @Override
-        public void run() {
-            Main.getUI().showWaitDialog();
-
-            try {
-                String pkgs[] = packages.split(",");
-
-                for (String pkg : pkgs) {
-                    Main.getUI().updateWaitDialog("Running monkey on " + pkg);
-
-                    try {
-                        // Stop running app.
-                        forceStop(pkg);
-
-                        // Little bit of breather here.
-                        try {
-                            Thread.sleep(1000);
-                        } catch (Exception e) {
-                        }
-
-                        DeviceUtils.doShell(device, "monkey -p " + pkg + " " + ITERATIONS, 1,
-                                TimeUnit.MINUTES);
-
-                        Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
-                        Map<String, String> data = Main.findAndGetClassData(device, pkg);
-                        DumpData dumpData = new DumpData(pkg, data, new Date());
-                        dataTableModel.addData(dumpData);
-                    } catch (Exception e) {
-                        e.printStackTrace();
-                    } finally {
-                        // Stop running app.
-                        forceStop(pkg);
-                    }
-                }
-            } finally {
-                Main.getUI().hideWaitDialog();
-            }
-        }
-
-        private void forceStop(String packageName) {
-            // Stop running app.
-            DeviceUtils.doShell(device, "force-stop " + packageName, 5, TimeUnit.SECONDS);
-            DeviceUtils.doShell(device, "kill " + packageName, 5, TimeUnit.SECONDS);
-            DeviceUtils.doShell(device, "kill `pid " + packageName + "`", 5, TimeUnit.SECONDS);
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java b/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java
deleted file mode 100644
index d74b8a3..0000000
--- a/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.util.Date;
-import java.util.Map;
-
-public class ScanAllPackagesAction extends AbstractThreadedDeviceSpecificAction {
-
-    private ClientUtils clientUtils;
-    private DumpTableModel dataTableModel;
-
-    public ScanAllPackagesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
-        super("Scan all packages", device);
-        this.clientUtils = utils;
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void run() {
-        Main.getUI().showWaitDialog();
-
-        try {
-            Client[] clients = clientUtils.findAllClients(device);
-            for (Client c : clients) {
-                String pkg = c.getClientData().getClientDescription();
-                Main.getUI().showWaitDialog();
-                Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
-
-                try {
-                    Map<String, String> data = Main.getClassDataRetriever().getClassData(c);
-                    DumpData dumpData = new DumpData(pkg, data, new Date());
-                    dataTableModel.addData(dumpData);
-                } catch (Exception e) {
-                    e.printStackTrace();
-                }
-            }
-        } finally {
-            Main.getUI().hideWaitDialog();
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java b/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java
deleted file mode 100644
index 98492bd..0000000
--- a/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.util.Date;
-import java.util.Map;
-
-public class ScanPackageAction extends AbstractThreadedDeviceSpecificAction {
-
-    private ClientUtils clientUtils;
-    private DumpTableModel dataTableModel;
-
-    public ScanPackageAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
-        super("Scan package", device);
-        this.clientUtils = utils;
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void run() {
-        Main.getUI().showWaitDialog();
-
-        try {
-            Client client = Main.getUI().getSelectedClient();
-            if (client != null) {
-                work(client);
-            } else {
-                Client[] clients = clientUtils.findAllClients(device);
-                if (clients.length > 0) {
-                    ClientWrapper[] clientWrappers = new ClientWrapper[clients.length];
-                    for (int i = 0; i < clientWrappers.length; i++) {
-                        clientWrappers[i] = new ClientWrapper(clients[i]);
-                    }
-                    Main.getUI().hideWaitDialog();
-
-                    ClientWrapper ret = Main.getUI().showChoiceDialog("Choose a package to scan",
-                            "Choose package",
-                            clientWrappers);
-                    if (ret != null) {
-                        work(ret.client);
-                    }
-                }
-            }
-        } finally {
-            Main.getUI().hideWaitDialog();
-        }
-    }
-
-    private void work(Client c) {
-        String pkg = c.getClientData().getClientDescription();
-        Main.getUI().showWaitDialog();
-        Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg);
-
-        try {
-            Map<String, String> data = Main.findAndGetClassData(device, pkg);
-            DumpData dumpData = new DumpData(pkg, data, new Date());
-            dataTableModel.addData(dumpData);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-    private static class ClientWrapper {
-        private Client client;
-
-        public ClientWrapper(Client c) {
-            client = c;
-        }
-
-        @Override
-        public String toString() {
-            return client.getClientData().getClientDescription() + " (pid "
-                    + client.getClientData().getPid() + ")";
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/ShowDataAction.java b/tools/preload2/src/com/android/preload/actions/ShowDataAction.java
deleted file mode 100644
index 2bb175f..0000000
--- a/tools/preload2/src/com/android/preload/actions/ShowDataAction.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.BorderLayout;
-import java.awt.event.ActionEvent;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.swing.AbstractAction;
-import javax.swing.JFrame;
-import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
-
-public class ShowDataAction extends AbstractAction {
-    private DumpTableModel dataTableModel;
-
-    public ShowDataAction(DumpTableModel dataTableModel) {
-        super("Show data");
-        this.dataTableModel = dataTableModel;
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        // TODO(agampe): Auto-generated method stub
-        int selRow = Main.getUI().getSelectedDataTableRow();
-        if (selRow != -1) {
-            DumpData data = dataTableModel.getData().get(selRow);
-            Map<String, Set<String>> inv = data.invertData();
-
-            StringBuilder builder = new StringBuilder();
-
-            // First bootclasspath.
-            add(builder, "Boot classpath:", inv.get(null));
-
-            // Now everything else.
-            for (String k : inv.keySet()) {
-                if (k != null) {
-                    builder.append("==================\n\n");
-                    add(builder, k, inv.get(k));
-                }
-            }
-
-            JFrame newFrame = new JFrame(data.getPackageName() + " " + data.getDate());
-            newFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
-            newFrame.getContentPane().add(new JScrollPane(new JTextArea(builder.toString())),
-                    BorderLayout.CENTER);
-            newFrame.setSize(800, 600);
-            newFrame.setLocationRelativeTo(null);
-            newFrame.setVisible(true);
-        }
-    }
-
-    private void add(StringBuilder builder, String head, Set<String> set) {
-        builder.append(head);
-        builder.append('\n');
-        addSet(builder, set);
-        builder.append('\n');
-    }
-
-    private void addSet(StringBuilder builder, Set<String> set) {
-        if (set == null) {
-            builder.append("  NONE\n");
-            return;
-        }
-        List<String> sorted = new ArrayList<>(set);
-        Collections.sort(sorted);
-        for (String s : sorted) {
-            builder.append(s);
-            builder.append('\n');
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java b/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java
deleted file mode 100644
index 9b97f11..0000000
--- a/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.actions;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.IDevice;
-import com.android.preload.ClientUtils;
-import com.android.preload.DeviceUtils;
-import com.android.preload.DumpData;
-import com.android.preload.DumpTableModel;
-import com.android.preload.Main;
-
-import java.awt.event.ActionEvent;
-import java.io.File;
-import java.util.Date;
-import java.util.Map;
-
-public class WritePreloadedClassesAction extends AbstractThreadedDeviceSpecificAction {
-    private File preloadedClassFile;
-
-    public WritePreloadedClassesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) {
-        super("Write preloaded classes action", device);
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent e) {
-        File[] files = Main.getUI().showOpenDialog(true);
-        if (files != null && files.length > 0) {
-            preloadedClassFile = files[0];
-            super.actionPerformed(e);
-        }
-    }
-
-    @Override
-    public void run() {
-        Main.getUI().showWaitDialog();
-        try {
-            // Write the new file with a 5-minute timeout
-            DeviceUtils.overwritePreloaded(device, preloadedClassFile, 5 * 60);
-        } catch (Exception e) {
-            System.err.println(e);
-        } finally {
-            Main.getUI().hideWaitDialog();
-        }
-    }
-}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java b/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java
deleted file mode 100644
index f04360f..0000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.classdataretrieval;
-
-import com.android.ddmlib.Client;
-
-import java.util.Map;
-
-/**
- * Retrieve a class-to-classloader map for loaded classes from the client.
- */
-public interface ClassDataRetriever {
-
-    public Map<String, String> getClassData(Client client);
-}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java
deleted file mode 100644
index 8d797ee..0000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.classdataretrieval.hprof;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData.IHprofDumpHandler;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class GeneralHprofDumpHandler implements IHprofDumpHandler {
-
-    private List<IHprofDumpHandler> handlers = new ArrayList<>();
-
-    public void addHandler(IHprofDumpHandler h) {
-      synchronized (handlers) {
-        handlers.add(h);
-      }
-    }
-
-    public void removeHandler(IHprofDumpHandler h) {
-      synchronized (handlers) {
-        handlers.remove(h);
-      }
-    }
-
-    private List<IHprofDumpHandler> getIterationList() {
-      synchronized (handlers) {
-        return new ArrayList<>(handlers);
-      }
-    }
-
-    @Override
-    public void onEndFailure(Client arg0, String arg1) {
-      List<IHprofDumpHandler> iterList = getIterationList();
-      for (IHprofDumpHandler h : iterList) {
-        h.onEndFailure(arg0, arg1);
-      }
-    }
-
-    @Override
-    public void onSuccess(String arg0, Client arg1) {
-      List<IHprofDumpHandler> iterList = getIterationList();
-      for (IHprofDumpHandler h : iterList) {
-        h.onSuccess(arg0, arg1);
-      }
-    }
-
-    @Override
-    public void onSuccess(byte[] arg0, Client arg1) {
-      List<IHprofDumpHandler> iterList = getIterationList();
-      for (IHprofDumpHandler h : iterList) {
-        h.onSuccess(arg0, arg1);
-      }
-    }
-  }
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java
deleted file mode 100644
index 84ec8b7..0000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.classdataretrieval.hprof;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData;
-import com.android.ddmlib.ClientData.IHprofDumpHandler;
-import com.android.preload.classdataretrieval.ClassDataRetriever;
-import com.android.preload.ui.NullProgressMonitor;
-import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
-import com.android.tools.perflib.heap.ClassObj;
-import com.android.tools.perflib.heap.Queries;
-import com.android.tools.perflib.heap.Snapshot;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-public class Hprof implements ClassDataRetriever {
-
-    private static GeneralHprofDumpHandler hprofHandler;
-
-    public static void init() {
-        synchronized(Hprof.class) {
-            if (hprofHandler == null) {
-                ClientData.setHprofDumpHandler(hprofHandler = new GeneralHprofDumpHandler());
-            }
-        }
-    }
-
-    public static File doHprof(Client client, int timeout) {
-        GetHprof gh = new GetHprof(client, timeout);
-        return gh.get();
-    }
-
-    /**
-     * Return a map of class names to class-loader names derived from the hprof dump.
-     *
-     * @param hprofLocalFile
-     */
-    public static Map<String, String> analyzeHprof(File hprofLocalFile) throws Exception {
-        Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprofLocalFile));
-
-        Map<String, Set<ClassObj>> classes = Queries.classes(snapshot, null);
-        Map<String, String> retValue = new HashMap<String, String>();
-        for (Map.Entry<String, Set<ClassObj>> e : classes.entrySet()) {
-            for (ClassObj c : e.getValue()) {
-                String cl = c.getClassLoader() == null ? null : c.getClassLoader().toString();
-                String cName = c.getClassName();
-                int aDepth = 0;
-                while (cName.endsWith("[]")) {
-                    cName = cName.substring(0, cName.length()-2);
-                    aDepth++;
-                }
-                String newName = transformPrimitiveClass(cName);
-                if (aDepth > 0) {
-                    // Need to use kind-a descriptor syntax. If it was transformed, it is primitive.
-                    if (newName.equals(cName)) {
-                        newName = "L" + newName + ";";
-                    }
-                    for (int i = 0; i < aDepth; i++) {
-                        newName = "[" + newName;
-                    }
-                }
-                retValue.put(newName, cl);
-            }
-        }
-
-        // Free up memory.
-        snapshot.dispose();
-
-        return retValue;
-    }
-
-    private static Map<String, String> primitiveMapping;
-
-    static {
-        primitiveMapping = new HashMap<>();
-        primitiveMapping.put("boolean", "Z");
-        primitiveMapping.put("byte", "B");
-        primitiveMapping.put("char", "C");
-        primitiveMapping.put("double", "D");
-        primitiveMapping.put("float", "F");
-        primitiveMapping.put("int", "I");
-        primitiveMapping.put("long", "J");
-        primitiveMapping.put("short", "S");
-        primitiveMapping.put("void", "V");
-    }
-
-    private static String transformPrimitiveClass(String name) {
-        String rep = primitiveMapping.get(name);
-        if (rep != null) {
-            return rep;
-        }
-        return name;
-    }
-
-    private static class GetHprof implements IHprofDumpHandler {
-
-        private File target;
-        private long timeout;
-        private Client client;
-
-        public GetHprof(Client client, long timeout) {
-            this.client = client;
-            this.timeout = timeout;
-        }
-
-        public File get() {
-            synchronized (this) {
-                hprofHandler.addHandler(this);
-                client.dumpHprof();
-                if (target == null) {
-                    try {
-                        wait(timeout);
-                    } catch (Exception e) {
-                        System.out.println(e);
-                    }
-                }
-            }
-
-            hprofHandler.removeHandler(this);
-            return target;
-        }
-
-        private void wakeUp() {
-            synchronized (this) {
-                notifyAll();
-            }
-        }
-
-        @Override
-        public void onEndFailure(Client arg0, String arg1) {
-            System.out.println("GetHprof.onEndFailure");
-            if (client == arg0) {
-                wakeUp();
-            }
-        }
-
-        private static File createTargetFile() {
-            try {
-                return File.createTempFile("ddms", ".hprof");
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        }
-
-        @Override
-        public void onSuccess(String arg0, Client arg1) {
-            System.out.println("GetHprof.onSuccess");
-            if (client == arg1) {
-                try {
-                    target = createTargetFile();
-                    arg1.getDevice().getSyncService().pullFile(arg0,
-                            target.getAbsoluteFile().toString(), new NullProgressMonitor());
-                } catch (Exception e) {
-                    if (target != null) {
-                        target.delete();
-                    }
-                    e.printStackTrace();
-                    target = null;
-                }
-                wakeUp();
-            }
-        }
-
-        @Override
-        public void onSuccess(byte[] arg0, Client arg1) {
-            System.out.println("GetHprof.onSuccess");
-            if (client == arg1) {
-                try {
-                    target = createTargetFile();
-                    BufferedOutputStream out =
-                            new BufferedOutputStream(new FileOutputStream(target));
-                    out.write(arg0);
-                    out.close();
-                } catch (Exception e) {
-                    if (target != null) {
-                        target.delete();
-                    }
-                    e.printStackTrace();
-                    target = null;
-                }
-                wakeUp();
-            }
-        }
-    }
-
-    private int timeout;
-
-    public Hprof(int timeout) {
-        this.timeout = timeout;
-    }
-
-    @Override
-    public Map<String, String> getClassData(Client client) {
-        File hprofLocalFile = Hprof.doHprof(client, timeout);
-        if (hprofLocalFile == null) {
-            throw new RuntimeException("Failed getting dump...");
-        }
-        System.out.println("Dump file is " + hprofLocalFile);
-
-        try {
-            return analyzeHprof(hprofLocalFile);
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        } finally {
-            hprofLocalFile.delete();
-        }
-    }
-}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java
deleted file mode 100644
index dbd4c89..0000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.classdataretrieval.jdwp;
-
-import com.android.ddmlib.Client;
-import com.android.preload.classdataretrieval.ClassDataRetriever;
-
-import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
-import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
-import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
-import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
-import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase;
-import org.apache.harmony.jpda.tests.jdwp.share.JDWPUnitDebuggeeWrapper;
-import org.apache.harmony.jpda.tests.share.JPDALogWriter;
-import org.apache.harmony.jpda.tests.share.JPDATestOptions;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class JDWPClassDataRetriever extends JDWPTestCase implements ClassDataRetriever {
-
-    private final Client client;
-
-    public JDWPClassDataRetriever() {
-        this(null);
-    }
-
-    public JDWPClassDataRetriever(Client client) {
-        this.client = client;
-    }
-
-
-    @Override
-    protected String getDebuggeeClassName() {
-        return "<unset>";
-    }
-
-    @Override
-    public Map<String, String> getClassData(Client client) {
-        return new JDWPClassDataRetriever(client).retrieve();
-    }
-
-    private Map<String, String> retrieve() {
-        if (client == null) {
-            throw new IllegalStateException();
-        }
-
-        settings = createTestOptions("localhost:" + String.valueOf(client.getDebuggerListenPort()));
-        settings.setDebuggeeSuspend("n");
-
-        logWriter = new JPDALogWriter(System.out, "", false);
-
-        try {
-            internalSetUp();
-
-            return retrieveImpl();
-        } catch (Exception e) {
-            e.printStackTrace();
-            return null;
-        } finally {
-            internalTearDown();
-        }
-    }
-
-    private Map<String, String> retrieveImpl() {
-        try {
-            // Suspend the app.
-            {
-                CommandPacket packet = new CommandPacket(
-                        JDWPCommands.VirtualMachineCommandSet.CommandSetID,
-                        JDWPCommands.VirtualMachineCommandSet.SuspendCommand);
-                ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
-                if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
-                    return null;
-                }
-            }
-
-            // List all classes.
-            CommandPacket packet = new CommandPacket(
-                    JDWPCommands.VirtualMachineCommandSet.CommandSetID,
-                    JDWPCommands.VirtualMachineCommandSet.AllClassesCommand);
-            ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
-
-            if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
-                return null;
-            }
-
-            int classCount = reply.getNextValueAsInt();
-            System.out.println("Runtime reported " + classCount + " classes.");
-
-            Map<Long, String> classes = new HashMap<Long, String>();
-            Map<Long, String> arrayClasses = new HashMap<Long, String>();
-
-            for (int i = 0; i < classCount; i++) {
-                byte refTypeTag = reply.getNextValueAsByte();
-                long typeID = reply.getNextValueAsReferenceTypeID();
-                String signature = reply.getNextValueAsString();
-                /* int status = */ reply.getNextValueAsInt();
-
-                switch (refTypeTag) {
-                    case JDWPConstants.TypeTag.CLASS:
-                    case JDWPConstants.TypeTag.INTERFACE:
-                        classes.put(typeID, signature);
-                        break;
-
-                    case JDWPConstants.TypeTag.ARRAY:
-                        arrayClasses.put(typeID, signature);
-                        break;
-                }
-            }
-
-            Map<String, String> result = new HashMap<String, String>();
-
-            // Parse all classes.
-            for (Map.Entry<Long, String> entry : classes.entrySet()) {
-                long typeID = entry.getKey();
-                String signature = entry.getValue();
-
-                if (!checkClass(typeID, signature, result)) {
-                    System.err.println("Issue investigating " + signature);
-                }
-            }
-
-            // For arrays, look at the leaf component type.
-            for (Map.Entry<Long, String> entry : arrayClasses.entrySet()) {
-                long typeID = entry.getKey();
-                String signature = entry.getValue();
-
-                if (!checkArrayClass(typeID, signature, result)) {
-                    System.err.println("Issue investigating " + signature);
-                }
-            }
-
-            return result;
-        } finally {
-            // Resume the app.
-            {
-                CommandPacket packet = new CommandPacket(
-                        JDWPCommands.VirtualMachineCommandSet.CommandSetID,
-                        JDWPCommands.VirtualMachineCommandSet.ResumeCommand);
-                /* ReplyPacket reply = */ debuggeeWrapper.vmMirror.performCommand(packet);
-            }
-        }
-    }
-
-    private boolean checkClass(long typeID, String signature, Map<String, String> result) {
-        CommandPacket packet = new CommandPacket(
-                JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
-                JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
-        packet.setNextValueAsReferenceTypeID(typeID);
-        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
-        if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
-            return false;
-        }
-
-        long classLoaderID = reply.getNextValueAsObjectID();
-
-        // TODO: Investigate the classloader to have a better string?
-        String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
-
-        result.put(getClassName(signature), classLoaderString);
-
-        return true;
-    }
-
-    private boolean checkArrayClass(long typeID, String signature, Map<String, String> result) {
-        // Classloaders of array classes are the same as the component class'.
-        CommandPacket packet = new CommandPacket(
-                JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
-                JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand);
-        packet.setNextValueAsReferenceTypeID(typeID);
-        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
-        if (reply.getErrorCode() != JDWPConstants.Error.NONE) {
-            return false;
-        }
-
-        long classLoaderID = reply.getNextValueAsObjectID();
-
-        // TODO: Investigate the classloader to have a better string?
-        String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID);
-
-        // For array classes, we *need* the signature directly.
-        result.put(signature, classLoaderString);
-
-        return true;
-    }
-
-    private static String getClassName(String signature) {
-        String withoutLAndSemicolon = signature.substring(1, signature.length() - 1);
-        return withoutLAndSemicolon.replace('/', '.');
-    }
-
-
-    private static JPDATestOptions createTestOptions(String address) {
-        JPDATestOptions options = new JPDATestOptions();
-        options.setAttachConnectorKind();
-        options.setTimeout(1000);
-        options.setWaitingTime(1000);
-        options.setTransportAddress(address);
-        return options;
-    }
-
-    @Override
-    protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() {
-        return new PreloadDebugeeWrapper(settings, logWriter);
-    }
-}
diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java
deleted file mode 100644
index b9df6d0..0000000
--- a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.classdataretrieval.jdwp;
-
-import org.apache.harmony.jpda.tests.framework.LogWriter;
-import org.apache.harmony.jpda.tests.jdwp.share.JDWPManualDebuggeeWrapper;
-import org.apache.harmony.jpda.tests.share.JPDATestOptions;
-
-import java.io.IOException;
-
-public class PreloadDebugeeWrapper extends JDWPManualDebuggeeWrapper {
-
-    public PreloadDebugeeWrapper(JPDATestOptions options, LogWriter writer) {
-        super(options, writer);
-    }
-
-    @Override
-    protected Process launchProcess(String cmdLine) throws IOException {
-        return null;
-    }
-
-    @Override
-    protected void WaitForProcessExit(Process process) {
-    }
-
-}
diff --git a/tools/preload2/src/com/android/preload/ui/IUI.java b/tools/preload2/src/com/android/preload/ui/IUI.java
deleted file mode 100644
index 9371463..0000000
--- a/tools/preload2/src/com/android/preload/ui/IUI.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.android.preload.ui;
-
-import com.android.ddmlib.Client;
-import java.io.File;
-import java.util.List;
-import javax.swing.Action;
-import javax.swing.ListModel;
-import javax.swing.table.TableModel;
-
-/**
- * UI abstraction for the tool. This allows a graphical mode, command line mode,
- * or silent mode.
- */
-public interface IUI {
-
-    void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
-            List<Action> actions);
-
-    void ready();
-
-    boolean isSingleThreaded();
-
-    Client getSelectedClient();
-
-    int getSelectedDataTableRow();
-
-    void showWaitDialog();
-
-    void updateWaitDialog(String s);
-
-    void hideWaitDialog();
-
-    void showMessageDialog(String s);
-
-    boolean showConfirmDialog(String title, String message);
-
-    String showInputDialog(String message);
-
-    <T> T showChoiceDialog(String title, String message, T[] choices);
-
-    File showSaveDialog();
-
-    File[] showOpenDialog(boolean multi);
-
-}
diff --git a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java b/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java
deleted file mode 100644
index f45aad0..0000000
--- a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.ui;
-
-import com.android.ddmlib.SyncService.ISyncProgressMonitor;
-
-public class NullProgressMonitor implements ISyncProgressMonitor {
-
-    @Override
-    public void advance(int arg0) {}
-
-    @Override
-    public boolean isCanceled() {
-        return false;
-    }
-
-    @Override
-    public void start(int arg0) {}
-
-    @Override
-    public void startSubTask(String arg0) {}
-
-    @Override
-    public void stop() {}
-}
\ No newline at end of file
diff --git a/tools/preload2/src/com/android/preload/ui/SequenceUI.java b/tools/preload2/src/com/android/preload/ui/SequenceUI.java
deleted file mode 100644
index dc6a4f3..0000000
--- a/tools/preload2/src/com/android/preload/ui/SequenceUI.java
+++ /dev/null
@@ -1,222 +0,0 @@
-package com.android.preload.ui;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData;
-import java.io.File;
-import java.util.LinkedList;
-import java.util.List;
-import javax.swing.Action;
-import javax.swing.ListModel;
-import javax.swing.table.TableModel;
-
-public class SequenceUI implements IUI {
-
-    private ListModel<Client> clientListModel;
-    @SuppressWarnings("unused")
-    private TableModel dataTableModel;
-    private List<Action> actions;
-
-    private List<Object> sequence = new LinkedList<>();
-
-    public SequenceUI() {
-    }
-
-    @Override
-    public boolean isSingleThreaded() {
-        return true;
-    }
-
-    @Override
-    public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
-            List<Action> actions) {
-        this.clientListModel = clientListModel;
-        this.dataTableModel = dataTableModel;
-        this.actions = actions;
-    }
-
-    public SequenceUI action(Action a) {
-        sequence.add(a);
-        return this;
-    }
-
-    public SequenceUI action(Class<? extends Action> actionClass) {
-        for (Action a : actions) {
-            if (actionClass.equals(a.getClass())) {
-                sequence.add(a);
-                return this;
-            }
-        }
-        throw new IllegalArgumentException("No action of class " + actionClass + " found.");
-    }
-
-    public SequenceUI confirmYes() {
-        sequence.add(Boolean.TRUE);
-        return this;
-    }
-
-    public SequenceUI confirmNo() {
-        sequence.add(Boolean.FALSE);
-        return this;
-    }
-
-    public SequenceUI input(String input) {
-        sequence.add(input);
-        return this;
-    }
-
-    public SequenceUI input(File... f) {
-        sequence.add(f);
-        return this;
-    }
-
-    public SequenceUI output(File f) {
-        sequence.add(f);
-        return this;
-    }
-
-    public SequenceUI tableRow(int i) {
-        sequence.add(i);
-        return this;
-    }
-
-    private class ClientSelector {
-        private String pkg;
-
-        public ClientSelector(String pkg) {
-            this.pkg = pkg;
-        }
-
-        public Client getClient() {
-            for (int i = 0; i < clientListModel.getSize(); i++) {
-                ClientData cd = clientListModel.getElementAt(i).getClientData();
-                if (cd != null) {
-                    String s = cd.getClientDescription();
-                    if (pkg.equals(s)) {
-                        return clientListModel.getElementAt(i);
-                    }
-                }
-            }
-            throw new RuntimeException("Didn't find client " + pkg);
-        }
-    }
-
-    public SequenceUI client(String pkg) {
-        sequence.add(new ClientSelector(pkg));
-        return this;
-    }
-
-    public SequenceUI choice(String pattern) {
-        sequence.add(pattern);
-        return this;
-    }
-
-    @Override
-    public void ready() {
-        // Run the actions.
-        // No iterator or foreach loop as the sequence will be emptied while running.
-        try {
-            while (!sequence.isEmpty()) {
-                Object next = sequence.remove(0);
-                if (next instanceof Action) {
-                    ((Action)next).actionPerformed(null);
-                } else {
-                    throw new IllegalStateException("Didn't expect a non-action: " + next);
-                }
-            }
-        } catch (Exception e) {
-            e.printStackTrace(System.out);
-        }
-
-        // Now shut down.
-        System.exit(0);
-    }
-
-    @Override
-    public Client getSelectedClient() {
-        Object next = sequence.remove(0);
-        if (next instanceof ClientSelector) {
-            return ((ClientSelector)next).getClient();
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-    @Override
-    public int getSelectedDataTableRow() {
-        Object next = sequence.remove(0);
-        if (next instanceof Integer) {
-            return ((Integer)next).intValue();
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-    @Override
-    public void showWaitDialog() {
-    }
-
-    @Override
-    public void updateWaitDialog(String s) {
-        System.out.println(s);
-    }
-
-    @Override
-    public void hideWaitDialog() {
-    }
-
-    @Override
-    public void showMessageDialog(String s) {
-        System.out.println(s);
-    }
-
-    @Override
-    public boolean showConfirmDialog(String title, String message) {
-        Object next = sequence.remove(0);
-        if (next instanceof Boolean) {
-            return ((Boolean)next).booleanValue();
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-    @Override
-    public String showInputDialog(String message) {
-        Object next = sequence.remove(0);
-        if (next instanceof String) {
-            return (String)next;
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-    @Override
-    public <T> T showChoiceDialog(String title, String message, T[] choices) {
-        Object next = sequence.remove(0);
-        if (next instanceof String) {
-            String s = (String)next;
-            for (T t : choices) {
-                if (t.toString().contains(s)) {
-                    return t;
-                }
-            }
-            return null;
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-    @Override
-    public File showSaveDialog() {
-        Object next = sequence.remove(0);
-        if (next instanceof File) {
-            System.out.println(next);
-            return (File)next;
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-    @Override
-    public File[] showOpenDialog(boolean multi) {
-        Object next = sequence.remove(0);
-        if (next instanceof File[]) {
-            return (File[])next;
-        }
-        throw new IllegalStateException("Unexpected: " + next);
-    }
-
-}
diff --git a/tools/preload2/src/com/android/preload/ui/SwingUI.java b/tools/preload2/src/com/android/preload/ui/SwingUI.java
deleted file mode 100644
index cab3744..0000000
--- a/tools/preload2/src/com/android/preload/ui/SwingUI.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright (C) 2015 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.preload.ui;
-
-import com.android.ddmlib.Client;
-import com.android.ddmlib.ClientData;
-
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.io.File;
-import java.util.List;
-
-import javax.swing.Action;
-import javax.swing.DefaultListCellRenderer;
-import javax.swing.JDialog;
-import javax.swing.JFileChooser;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JList;
-import javax.swing.JOptionPane;
-import javax.swing.JProgressBar;
-import javax.swing.JScrollPane;
-import javax.swing.JTable;
-import javax.swing.JToolBar;
-import javax.swing.ListModel;
-import javax.swing.SwingUtilities;
-import javax.swing.table.TableModel;
-
-public class SwingUI extends JFrame implements IUI {
-
-    private JList<Client> clientList;
-    private JTable dataTable;
-
-    // Shared file chooser, means the directory is retained.
-    private JFileChooser jfc;
-
-    public SwingUI() {
-        super("Preloaded-classes computation");
-    }
-
-    @Override
-    public boolean isSingleThreaded() {
-        return false;
-    }
-
-    @Override
-    public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
-            List<Action> actions) {
-        getContentPane().add(new JScrollPane(clientList = new JList<Client>(clientListModel)),
-                BorderLayout.WEST);
-        clientList.setCellRenderer(new ClientListCellRenderer());
-        // clientList.addListSelectionListener(listener);
-
-        dataTable = new JTable(dataTableModel);
-        getContentPane().add(new JScrollPane(dataTable), BorderLayout.CENTER);
-
-        JToolBar toolbar = new JToolBar(JToolBar.HORIZONTAL);
-        for (Action a : actions) {
-            if (a == null) {
-                toolbar.addSeparator();
-            } else {
-                toolbar.add(a);
-            }
-        }
-        getContentPane().add(toolbar, BorderLayout.PAGE_START);
-
-        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
-        setBounds(100, 100, 800, 600);
-
-        setVisible(true);
-    }
-
-    @Override
-    public void ready() {
-    }
-
-    @Override
-    public Client getSelectedClient() {
-        return clientList.getSelectedValue();
-    }
-
-    @Override
-    public int getSelectedDataTableRow() {
-        return dataTable.getSelectedRow();
-    }
-
-    private JDialog currentWaitDialog = null;
-
-    @Override
-    public void showWaitDialog() {
-        if (currentWaitDialog == null) {
-            currentWaitDialog = new JDialog(this, "Please wait...", true);
-            currentWaitDialog.getContentPane().add(new JLabel("Please be patient."),
-                    BorderLayout.CENTER);
-            JProgressBar progress = new JProgressBar(JProgressBar.HORIZONTAL);
-            progress.setIndeterminate(true);
-            currentWaitDialog.getContentPane().add(progress, BorderLayout.SOUTH);
-            currentWaitDialog.setSize(200, 100);
-            currentWaitDialog.setLocationRelativeTo(null);
-            showWaitDialogLater();
-        }
-    }
-
-    private void showWaitDialogLater() {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                if (currentWaitDialog != null) {
-                    currentWaitDialog.setVisible(true); // This is blocking.
-                }
-            }
-        });
-    }
-
-    @Override
-    public void updateWaitDialog(String s) {
-        if (currentWaitDialog != null) {
-            ((JLabel) currentWaitDialog.getContentPane().getComponent(0)).setText(s);
-            Dimension prefSize = currentWaitDialog.getPreferredSize();
-            Dimension curSize = currentWaitDialog.getSize();
-            if (prefSize.width > curSize.width || prefSize.height > curSize.height) {
-                currentWaitDialog.setSize(Math.max(prefSize.width, curSize.width),
-                        Math.max(prefSize.height, curSize.height));
-                currentWaitDialog.invalidate();
-            }
-        }
-    }
-
-    @Override
-    public void hideWaitDialog() {
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-            currentWaitDialog = null;
-        }
-    }
-
-    @Override
-    public void showMessageDialog(String s) {
-        // Hide the wait dialog...
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-        }
-
-        try {
-            JOptionPane.showMessageDialog(this, s);
-        } finally {
-            // And reshow it afterwards...
-            if (currentWaitDialog != null) {
-                showWaitDialogLater();
-            }
-        }
-    }
-
-    @Override
-    public boolean showConfirmDialog(String title, String message) {
-        // Hide the wait dialog...
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-        }
-
-        try {
-            return JOptionPane.showConfirmDialog(this, title, message, JOptionPane.YES_NO_OPTION)
-                    == JOptionPane.YES_OPTION;
-        } finally {
-            // And reshow it afterwards...
-            if (currentWaitDialog != null) {
-                showWaitDialogLater();
-            }
-        }
-    }
-
-    @Override
-    public String showInputDialog(String message) {
-        // Hide the wait dialog...
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-        }
-
-        try {
-            return JOptionPane.showInputDialog(message);
-        } finally {
-            // And reshow it afterwards...
-            if (currentWaitDialog != null) {
-                showWaitDialogLater();
-            }
-        }
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <T> T showChoiceDialog(String title, String message, T[] choices) {
-        // Hide the wait dialog...
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-        }
-
-        try{
-            return (T)JOptionPane.showInputDialog(this,
-                    title,
-                    message,
-                    JOptionPane.QUESTION_MESSAGE,
-                    null,
-                    choices,
-                    choices[0]);
-        } finally {
-            // And reshow it afterwards...
-            if (currentWaitDialog != null) {
-                showWaitDialogLater();
-            }
-        }
-    }
-
-    @Override
-    public File showSaveDialog() {
-        // Hide the wait dialog...
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-        }
-
-        try{
-            if (jfc == null) {
-                jfc = new JFileChooser();
-            }
-
-            int ret = jfc.showSaveDialog(this);
-            if (ret == JFileChooser.APPROVE_OPTION) {
-                return jfc.getSelectedFile();
-            } else {
-                return null;
-            }
-        } finally {
-            // And reshow it afterwards...
-            if (currentWaitDialog != null) {
-                showWaitDialogLater();
-            }
-        }
-    }
-
-    @Override
-    public File[] showOpenDialog(boolean multi) {
-        // Hide the wait dialog...
-        if (currentWaitDialog != null) {
-            currentWaitDialog.setVisible(false);
-        }
-
-        try{
-            if (jfc == null) {
-                jfc = new JFileChooser();
-            }
-
-            jfc.setMultiSelectionEnabled(multi);
-            int ret = jfc.showOpenDialog(this);
-            if (ret == JFileChooser.APPROVE_OPTION) {
-                return jfc.getSelectedFiles();
-            } else {
-                return null;
-            }
-        } finally {
-            // And reshow it afterwards...
-            if (currentWaitDialog != null) {
-                showWaitDialogLater();
-            }
-        }
-    }
-
-    private class ClientListCellRenderer extends DefaultListCellRenderer {
-
-        @Override
-        public Component getListCellRendererComponent(JList<?> list, Object value, int index,
-                boolean isSelected, boolean cellHasFocus) {
-            ClientData cd = ((Client) value).getClientData();
-            String s = cd.getClientDescription() + " (pid " + cd.getPid() + ")";
-            return super.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus);
-        }
-    }
-}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 89ed080..2f7400d 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -671,6 +671,14 @@
     public static final int IFACE_IP_MODE_LOCAL_ONLY = 2;
 
     /**
+     * Broadcast intent action indicating that the wifi network settings
+     * had been reset.
+     * @hide
+     */
+    public static final String WIFI_NETWORK_SETTINGS_RESET_ACTION =
+            "android.net.wifi.action.NETWORK_SETTINGS_RESET";
+
+    /**
      * Broadcast intent action indicating that a connection to the supplicant has
      * been established (and it is now possible
      * to perform Wi-Fi operations) or the connection to the supplicant has been