Merge "allow error notification in plugin framework to be shown in external process, such as Launcher"
diff --git a/Android.bp b/Android.bp
index b2ed0de..73eea8c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -26,48 +26,166 @@
 // READ ME: ########################################################
 
 filegroup {
-    name: "framework-defaults-java-srcs",
+    name: "framework-core-sources",
     srcs: [
-        // java sources under this directory
         "core/java/**/*.java",
-        "drm/java/**/*.java",
-        "graphics/java/**/*.java",
-        "keystore/java/**/*.java",
-        "location/java/**/*.java",
-        "lowpan/java/**/*.java",
-        "media/java/**/*.java",
-        "media/mca/effect/java/**/*.java",
-        "media/mca/filterfw/java/**/*.java",
-        "media/mca/filterpacks/java/**/*.java",
-        "opengl/java/**/*.java",
-        "rs/java/**/*.java",
-        "sax/java/**/*.java",
-        "telecomm/java/**/*.java",
-        "telephony/java/**/*.java",
-        "wifi/java/**/*.java",
+        "core/java/**/*.aidl",
     ],
+    path: "core/java",
 }
 
-// TODO(b/70046217): make these as filegroups where the base directory for aidl files
-// is given as 'path'. Eliminate the need for aidl_local_include_dirs.
-framework_srcs = [
-    // aidl under this directory
-    // b/70046217#comment15 These MUST come after all java srcs.
-    // TODO(b/70046217) remove the above requirement
-    "core/java/**/*.aidl",
-    "graphics/java/**/*.aidl",
-    "keystore/java/**/*.aidl",
-    "location/java/**/*.aidl",
-    "lowpan/java/**/*.aidl",
-    "media/java/**/*.aidl",
-    "packages/services/PacProcessor/**/*.aidl",
-    "packages/services/Proxy/**/*.aidl",
-    "telecomm/java/**/*.aidl",
-    "telephony/java/**/*.aidl",
-    "wifi/java/**/*.aidl",
+filegroup {
+    name: "framework-drm-sources",
+    srcs: [
+        "drm/java/**/*.java",
+    ],
+    path: "drm/java",
+}
 
-    // aidl from external directories
+filegroup {
+    name: "framework-graphics-sources",
+    srcs: [
+        "graphics/java/**/*.java",
+        "graphics/java/**/*.aidl",
+    ],
+    path: "graphics/java",
+}
+
+filegroup {
+    name: "framework-keystore-sources",
+    srcs: [
+        "keystore/java/**/*.java",
+        "keystore/java/**/*.aidl",
+    ],
+    path: "keystore/java",
+}
+
+filegroup {
+    name: "framework-location-sources",
+    srcs: [
+        "location/java/**/*.java",
+        "location/java/**/*.aidl",
+    ],
+    path: "location/java",
+}
+
+filegroup {
+    name: "framework-lowpan-sources",
+    srcs: [
+        "lowpan/java/**/*.java",
+        "lowpan/java/**/*.aidl",
+    ],
+    path: "lowpan/java",
+}
+
+filegroup {
+    name: "framework-media-sources",
+    srcs: [
+        "media/java/**/*.java",
+        "media/java/**/*.aidl",
+    ],
+    path: "media/java",
+}
+
+filegroup {
+    name: "framework-mca-effect-sources",
+    srcs: [
+        "media/mca/effect/java/**/*.java",
+    ],
+    path: "media/mca/effect/java",
+}
+
+filegroup {
+    name: "framework-mca-filterfw-sources",
+    srcs: [
+        "media/mca/filterfw/java/**/*.java",
+    ],
+    path: "media/mca/filterfw/java",
+}
+
+filegroup {
+    name: "framework-mca-filterpacks-sources",
+    srcs: [
+        "media/mca/filterpacks/java/**/*.java",
+    ],
+    path: "media/mca/filterpacks/java",
+}
+
+filegroup {
+    name: "framework-opengl-sources",
+    srcs: [
+        "opengl/java/**/*.java",
+    ],
+    path: "opengl/java",
+}
+
+filegroup {
+    name: "framework-rs-sources",
+    srcs: [
+        "rs/java/**/*.java",
+    ],
+    path: "rs/java",
+}
+
+filegroup {
+    name: "framework-sax-sources",
+    srcs: [
+        "sax/java/**/*.java",
+    ],
+    path: "sax/java",
+}
+
+filegroup {
+    name: "framework-telecomm-sources",
+    srcs: [
+        "telecomm/java/**/*.java",
+        "telecomm/java/**/*.aidl",
+    ],
+    path: "telecomm/java",
+}
+
+filegroup {
+    name: "framework-telephony-sources",
+    srcs: [
+        "telephony/java/**/*.java",
+        "telephony/java/**/*.aidl",
+    ],
+    path: "telephony/java",
+}
+
+filegroup {
+    name: "framework-wifi-sources",
+    srcs: [
+        "wifi/java/**/*.java",
+        "wifi/java/**/*.aidl",
+    ],
+    path: "wifi/java",
+}
+
+framework_srcs = [
+    // Java/AIDL sources under frameworks/base
+    ":framework-core-sources",
+    ":framework-drm-sources",
+    ":framework-graphics-sources",
+    ":framework-keystore-sources",
+    ":framework-location-sources",
+    ":framework-lowpan-sources",
+    ":framework-media-sources",
+    ":framework-mca-effect-sources",
+    ":framework-mca-filterfw-sources",
+    ":framework-mca-filterpacks-sources",
+    ":framework-opengl-sources",
+    ":framework-rs-sources",
+    ":framework-sax-sources",
+    ":framework-telecomm-sources",
+    ":framework-telephony-sources",
+    ":framework-wifi-sources",
+    ":PacProcessor-aidl-sources",
+    ":ProxyHandler-aidl-sources",
+
+    // AIDL sources from external directories
     ":dumpstate_aidl",
+    ":framework_native_aidl",
     ":gatekeeper_aidl",
     ":gsiservice_aidl",
     ":incidentcompanion_aidl",
@@ -88,47 +206,28 @@
     ":framework-statslog-gen",
 ]
 
-framework_aidl_local_include_dirs = [
-    "core/java",
-    "drm/java",
-    "graphics/java",
-    "keystore/java",
-    "location/java",
-    "lowpan/java",
-    "media/java",
-    "media/apex/java",
-    "media/mca/effect/java",
-    "media/mca/filterfw/java",
-    "media/mca/filterpacks/java",
-    "opengl/java",
-    "rs/java",
-    "sax/java",
-    "telecomm/java",
-    "telephony/java",
-    "wifi/java",
-]
-
-framework_aidl_external_include_dirs = [
-    "frameworks/av/camera/aidl",
-    "frameworks/av/media/libaudioclient/aidl",
-    "frameworks/native/aidl/binder",
-    "frameworks/native/aidl/gui",
-    "frameworks/native/cmds/dumpstate/binder",
-    "frameworks/native/libs/incidentcompanion/binder",
-    "system/bt/binder",
-    "system/core/gatekeeperd/binder",
-    "system/core/storaged/binder",
-    "system/gsid/aidl",
-    "system/security/keystore/binder",
-    "system/update_engine/binder_bindings",
-    "system/vold/binder",
-]
-
 java_defaults {
     name: "framework-aidl-export-defaults",
-
     aidl: {
-        export_include_dirs: framework_aidl_local_include_dirs,
+        export_include_dirs: [
+            "core/java",
+            "drm/java",
+            "graphics/java",
+            "keystore/java",
+            "location/java",
+            "lowpan/java",
+            "media/java",
+            "media/apex/java",
+            "media/mca/effect/java",
+            "media/mca/filterfw/java",
+            "media/mca/filterpacks/java",
+            "opengl/java",
+            "rs/java",
+            "sax/java",
+            "telecomm/java",
+            "telephony/java",
+            "wifi/java",
+        ],
     },
 }
 
@@ -137,13 +236,9 @@
     defaults: ["framework-aidl-export-defaults"],
     installable: true,
 
-    srcs: [
-        ":framework-defaults-java-srcs",
-    ] + framework_srcs,
+    srcs: framework_srcs,
 
     aidl: {
-        local_include_dirs: framework_aidl_local_include_dirs,
-        include_dirs: framework_aidl_external_include_dirs,
         generate_get_transaction_name: true,
     },
 
@@ -793,7 +888,6 @@
         ":jobscheduler-framework-source",
     ],
     srcs_lib: "framework-minus-apex",
-    srcs_lib_whitelist_dirs: frameworks_base_subdirs,
     srcs_lib_whitelist_pkgs: packages_to_document,
     libs: framework_docs_only_libs,
     local_sourcepaths: frameworks_base_subdirs,
@@ -851,7 +945,6 @@
         ":jobscheduler-framework-source",
     ],
     srcs_lib: "framework-minus-apex",
-    srcs_lib_whitelist_dirs: frameworks_base_subdirs,
     srcs_lib_whitelist_pkgs: packages_to_document,
     local_sourcepaths: frameworks_base_subdirs,
     installable: false,
@@ -1173,7 +1266,6 @@
     name: "hiddenapi-mappings",
     defaults: ["metalava-api-stubs-default"],
     srcs: [
-        ":framework-defaults-java-srcs",
         ":non_openjdk_java_files",
         ":openjdk_java_files",
         ":opt-telephony-common-srcs",
@@ -1313,8 +1405,6 @@
 aidl_mapping {
     name: "framework-aidl-mappings",
     srcs: framework_srcs,
-    local_include_dirs: framework_aidl_local_include_dirs,
-    include_dirs: framework_aidl_external_include_dirs,
     output: "framework-aidl-mappings.txt",
 }
 
diff --git a/api/current.txt b/api/current.txt
index 652f23f..8a6d2bf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4278,6 +4278,7 @@
     method @Deprecated public int checkOpNoThrow(@NonNull String, int, @NonNull String);
     method public void checkPackage(int, @NonNull String);
     method public void finishOp(@NonNull String, int, @NonNull String);
+    method public boolean isOpActive(@NonNull String, int, @NonNull String);
     method public int noteOp(@NonNull String, int, @NonNull String);
     method public int noteOpNoThrow(@NonNull String, int, @NonNull String);
     method public int noteProxyOp(@NonNull String, @NonNull String);
@@ -4286,8 +4287,10 @@
     method public static String permissionToOp(String);
     method public int startOp(@NonNull String, int, @NonNull String);
     method public int startOpNoThrow(@NonNull String, int, @NonNull String);
+    method public void startWatchingActive(@NonNull String[], @NonNull java.util.concurrent.Executor, @NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
     method public void startWatchingMode(@NonNull String, @Nullable String, @NonNull android.app.AppOpsManager.OnOpChangedListener);
     method public void startWatchingMode(@NonNull String, @Nullable String, int, @NonNull android.app.AppOpsManager.OnOpChangedListener);
+    method public void stopWatchingActive(@NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
     method public void stopWatchingMode(@NonNull android.app.AppOpsManager.OnOpChangedListener);
     method public int unsafeCheckOp(@NonNull String, int, @NonNull String);
     method public int unsafeCheckOpNoThrow(@NonNull String, int, @NonNull String);
@@ -4335,6 +4338,10 @@
     field public static final int WATCH_FOREGROUND_CHANGES = 1; // 0x1
   }
 
+  public static interface AppOpsManager.OnOpActiveChangedListener {
+    method public void onOpActiveChanged(@NonNull String, int, @NonNull String, boolean);
+  }
+
   public static interface AppOpsManager.OnOpChangedListener {
     method public void onOpChanged(String, String);
   }
@@ -9341,6 +9348,7 @@
     field public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
     field public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
     field public static final String MIMETYPE_TEXT_URILIST = "text/uri-list";
+    field public static final String MIMETYPE_UNKNOWN = "application/octet-stream";
   }
 
   public class ClipboardManager extends android.text.ClipboardManager {
@@ -9688,6 +9696,7 @@
     method public Long getAsLong(String);
     method public Short getAsShort(String);
     method public String getAsString(String);
+    method public boolean isEmpty();
     method public java.util.Set<java.lang.String> keySet();
     method public void put(String, String);
     method public void put(String, Byte);
@@ -34523,6 +34532,7 @@
     method public static String getExternalStorageState();
     method public static String getExternalStorageState(java.io.File);
     method @NonNull public static java.io.File getRootDirectory();
+    method @NonNull public static java.io.File getStorageDirectory();
     method @Deprecated public static String getStorageState(java.io.File);
     method public static boolean isExternalStorageEmulated();
     method public static boolean isExternalStorageEmulated(@NonNull java.io.File);
@@ -38346,8 +38356,10 @@
     ctor public MediaStore();
     method @Nullable public static android.net.Uri getDocumentUri(@NonNull android.content.Context, @NonNull android.net.Uri);
     method @NonNull public static java.util.Set<java.lang.String> getExternalVolumeNames(@NonNull android.content.Context);
+    method public static boolean getIncludePending(@NonNull android.net.Uri);
     method public static android.net.Uri getMediaScannerUri();
     method @Nullable public static android.net.Uri getMediaUri(@NonNull android.content.Context, @NonNull android.net.Uri);
+    method public static boolean getRequireOriginal(@NonNull android.net.Uri);
     method @NonNull public static String getVersion(@NonNull android.content.Context);
     method @NonNull public static String getVersion(@NonNull android.content.Context, @NonNull String);
     method @NonNull public static String getVolumeName(@NonNull android.net.Uri);
@@ -38490,6 +38502,7 @@
   public static final class MediaStore.Audio.Media implements android.provider.MediaStore.Audio.AudioColumns {
     ctor public MediaStore.Audio.Media();
     method public static android.net.Uri getContentUri(String);
+    method @NonNull public static android.net.Uri getContentUri(@NonNull String, long);
     method @Deprecated @Nullable public static android.net.Uri getContentUriForPath(@NonNull String);
     field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/audio";
     field public static final String DEFAULT_SORT_ORDER = "title_key";
@@ -38540,6 +38553,7 @@
 
   public static final class MediaStore.Downloads implements android.provider.MediaStore.DownloadColumns {
     method @NonNull public static android.net.Uri getContentUri(@NonNull String);
+    method @NonNull public static android.net.Uri getContentUri(@NonNull String, long);
     field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/download";
     field @NonNull public static final android.net.Uri EXTERNAL_CONTENT_URI;
     field @NonNull public static final android.net.Uri INTERNAL_CONTENT_URI;
@@ -38580,6 +38594,7 @@
     ctor public MediaStore.Images.Media();
     method @Deprecated public static android.graphics.Bitmap getBitmap(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException, java.io.IOException;
     method public static android.net.Uri getContentUri(String);
+    method @NonNull public static android.net.Uri getContentUri(@NonNull String, long);
     method @Deprecated public static String insertImage(android.content.ContentResolver, String, String, String) throws java.io.FileNotFoundException;
     method @Deprecated public static String insertImage(android.content.ContentResolver, android.graphics.Bitmap, String, String);
     method @Deprecated public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]);
@@ -38649,6 +38664,7 @@
   public static final class MediaStore.Video.Media implements android.provider.MediaStore.Video.VideoColumns {
     ctor public MediaStore.Video.Media();
     method public static android.net.Uri getContentUri(String);
+    method @NonNull public static android.net.Uri getContentUri(@NonNull String, long);
     field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/video";
     field public static final String DEFAULT_SORT_ORDER = "title";
     field public static final android.net.Uri EXTERNAL_CONTENT_URI;
diff --git a/api/removed.txt b/api/removed.txt
index 74c1d3c..db784a8 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -481,9 +481,8 @@
   @Deprecated public static class MediaStore.PendingParams {
     ctor public MediaStore.PendingParams(@NonNull android.net.Uri, @NonNull String, @NonNull String);
     method public void setDownloadUri(@Nullable android.net.Uri);
-    method public void setPrimaryDirectory(@Nullable String);
     method public void setRefererUri(@Nullable android.net.Uri);
-    method public void setSecondaryDirectory(@Nullable String);
+    method public void setRelativePath(@Nullable String);
   }
 
   @Deprecated public static class MediaStore.PendingSession implements java.lang.AutoCloseable {
diff --git a/api/system-current.txt b/api/system-current.txt
index ed2616a..86e5f2e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -353,6 +353,9 @@
     field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
     field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
     field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
+    field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
+    field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
+    field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
     field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
     field public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
     field public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
@@ -368,6 +371,9 @@
     field public static final String OPSTR_WIFI_SCAN = "android:wifi_scan";
     field public static final String OPSTR_WRITE_CLIPBOARD = "android:write_clipboard";
     field public static final String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
+    field public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio";
+    field public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
+    field public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video";
     field public static final String OPSTR_WRITE_SMS = "android:write_sms";
     field public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
     field public static final int OP_FLAGS_ALL = 31; // 0x1f
diff --git a/api/test-current.txt b/api/test-current.txt
index 1b2c59d..7e1c67d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -160,8 +160,6 @@
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(int, int, String, int);
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(String, int, String, int);
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(String, int, int);
-    method public void startWatchingActive(@NonNull int[], @NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
-    method public void stopWatchingActive(@NonNull android.app.AppOpsManager.OnOpActiveChangedListener);
     method public static int strOpToOp(@NonNull String);
     field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0
     field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1
@@ -193,6 +191,9 @@
     field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
     field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
     field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
+    field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
+    field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
+    field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
     field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
     field public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
     field public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
@@ -208,6 +209,9 @@
     field public static final String OPSTR_WIFI_SCAN = "android:wifi_scan";
     field public static final String OPSTR_WRITE_CLIPBOARD = "android:write_clipboard";
     field public static final String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
+    field public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio";
+    field public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
+    field public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video";
     field public static final String OPSTR_WRITE_SMS = "android:write_sms";
     field public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
     field public static final int OP_COARSE_LOCATION = 0; // 0x0
@@ -293,10 +297,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalUidOps> CREATOR;
   }
 
-  public static interface AppOpsManager.OnOpActiveChangedListener {
-    method public void onOpActiveChanged(int, int, String, boolean);
-  }
-
   public static final class AppOpsManager.OpEntry implements android.os.Parcelable {
     method public int describeContents();
     method public long getDuration();
@@ -664,6 +664,7 @@
     method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions);
     field public static final String BUGREPORT_SERVICE = "bugreport";
     field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
+    field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
     field public static final String PERMISSION_SERVICE = "permission";
     field public static final String ROLLBACK_SERVICE = "rollback";
     field public static final String STATUS_BAR_SERVICE = "statusbar";
@@ -1736,7 +1737,6 @@
   public class Environment {
     method public static java.io.File buildPath(java.io.File, java.lang.String...);
     method @NonNull public static java.io.File getProductDirectory();
-    method @NonNull public static java.io.File getStorageDirectory();
   }
 
   public final class FileUtils {
diff --git a/cmds/incidentd/src/WorkDirectory.cpp b/cmds/incidentd/src/WorkDirectory.cpp
index 0570c3a..8dcb865 100644
--- a/cmds/incidentd/src/WorkDirectory.cpp
+++ b/cmds/incidentd/src/WorkDirectory.cpp
@@ -664,7 +664,7 @@
             nanosleep(&spec, nullptr);
         }
         clock_gettime(CLOCK_REALTIME, &spec);
-        timestampNs = (spec.tv_sec) * 1000 + spec.tv_nsec;
+        timestampNs = int64_t(spec.tv_sec) * 1000 + spec.tv_nsec;
     } while (file_exists_locked(timestampNs));
     return timestampNs;
 }
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 6df0a8e..19fa640 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -129,17 +129,12 @@
         "libservices",
         "libprotoutil",
         "libstatslog",
-        "libhardware",
-        "libhardware_legacy",
         "libhidlbase",
-        "libhidltransport",
-        "libhwbinder",
         "android.frameworks.stats@1.0",
         "android.hardware.health@2.0",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
         "android.hardware.power.stats@1.0",
-        "libpackagelistparser",
         "libsysutils",
         "libcutils",
     ],
@@ -253,9 +248,6 @@
         "tests/e2e/GaugeMetric_e2e_push_test.cpp",
         "tests/e2e/GaugeMetric_e2e_pull_test.cpp",
         "tests/e2e/ValueMetric_pull_e2e_test.cpp",
-        "tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp",
-        "tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp",
-        "tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp",
         "tests/e2e/Anomaly_count_e2e_test.cpp",
         "tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
         "tests/e2e/ConfigTtl_e2e_test.cpp",
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index 60a4b23..52a1269 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -110,20 +110,14 @@
 
 void CombinationConditionTracker::isConditionMet(
         const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
-        const std::vector<Matcher>& dimensionFields,
-        const bool isSubOutputDimensionFields,
         const bool isPartialLink,
-        vector<ConditionState>& conditionCache,
-        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
+        vector<ConditionState>& conditionCache) const {
     // So far, this is fine as there is at most one child having sliced output.
     for (const int childIndex : mChildren) {
         if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
             allConditions[childIndex]->isConditionMet(conditionParameters, allConditions,
-                                                      dimensionFields,
-                                                      isSubOutputDimensionFields,
                                                       isPartialLink,
-                                                      conditionCache,
-                                                      dimensionsKeySet);
+                                                      conditionCache);
         }
     }
     conditionCache[mIndex] =
@@ -178,25 +172,6 @@
     }
 }
 
-ConditionState CombinationConditionTracker::getMetConditionDimension(
-        const std::vector<sp<ConditionTracker>>& allConditions,
-        const std::vector<Matcher>& dimensionFields,
-        const bool isSubOutputDimensionFields,
-        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
-    vector<ConditionState> conditionCache(allConditions.size(), ConditionState::kNotEvaluated);
-    // So far, this is fine as there is at most one child having sliced output.
-    for (const int childIndex : mChildren) {
-        conditionCache[childIndex] = conditionCache[childIndex] |
-            allConditions[childIndex]->getMetConditionDimension(
-                allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet);
-    }
-    evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
-    if (conditionCache[mIndex] == ConditionState::kTrue && dimensionsKeySet.empty()) {
-        dimensionsKeySet.insert(DEFAULT_DIMENSION_KEY);
-    }
-    return conditionCache[mIndex];
-}
-
 bool CombinationConditionTracker::equalOutputDimensions(
         const std::vector<sp<ConditionTracker>>& allConditions,
         const vector<Matcher>& dimensions) const {
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index 481cb20..e3d8601 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -43,17 +43,8 @@
 
     void isConditionMet(const ConditionKey& conditionParameters,
                         const std::vector<sp<ConditionTracker>>& allConditions,
-                        const vector<Matcher>& dimensionFields,
-                        const bool isSubOutputDimensionFields,
                         const bool isPartialLink,
-                        std::vector<ConditionState>& conditionCache,
-                        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
-
-    ConditionState getMetConditionDimension(
-            const std::vector<sp<ConditionTracker>>& allConditions,
-            const vector<Matcher>& dimensionFields,
-            const bool isSubOutputDimensionFields,
-            std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+                        std::vector<ConditionState>& conditionCache) const override;
 
     // Only one child predicate can have dimension.
     const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 1f4266b..e94ea65 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -84,29 +84,14 @@
     //                       condition.
     // [allConditions]: all condition trackers. This is needed because the condition evaluation is
     //                  done recursively
-    // [dimensionFields]: the needed dimension fields which should be all or subset of the condition
-    //                    tracker output dimension.
-    // [isSubOutputDimensionFields]: true if the needed dimension fields which is strictly subset of
-    //                               the condition tracker output dimension.
     // [isPartialLink]: true if the link specified by 'conditionParameters' contains all the fields
     //                  in the condition tracker output dimension.
     // [conditionCache]: the cache holding the condition evaluation values.
-    // [dimensionsKeySet]: the dimensions where the sliced condition is true. For combination
-    //                    condition, it assumes that only one child predicate is sliced.
     virtual void isConditionMet(
             const ConditionKey& conditionParameters,
             const std::vector<sp<ConditionTracker>>& allConditions,
-            const vector<Matcher>& dimensionFields,
-            const bool isSubOutputDimensionFields,
             const bool isPartialLink,
-            std::vector<ConditionState>& conditionCache,
-            std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const = 0;
-
-    virtual ConditionState getMetConditionDimension(
-            const std::vector<sp<ConditionTracker>>& allConditions,
-            const vector<Matcher>& dimensionFields,
-            const bool isSubOutputDimensionFields,
-            std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const = 0;
+            std::vector<ConditionState>& conditionCache) const = 0;
 
     // return the list of LogMatchingTracker index that this ConditionTracker uses.
     virtual const std::set<int>& getLogTrackerIndex() const {
diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp
index 23a9d37..4f44a69 100644
--- a/cmds/statsd/src/condition/ConditionWizard.cpp
+++ b/cmds/statsd/src/condition/ConditionWizard.cpp
@@ -25,27 +25,15 @@
 using std::vector;
 
 ConditionState ConditionWizard::query(const int index, const ConditionKey& parameters,
-                                      const vector<Matcher>& dimensionFields,
-                                      const bool isSubOutputDimensionFields,
-                                      const bool isPartialLink,
-                                      std::unordered_set<HashableDimensionKey>* dimensionKeySet) {
+                                      const bool isPartialLink) {
     vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
 
     mAllConditions[index]->isConditionMet(
-        parameters, mAllConditions, dimensionFields, isSubOutputDimensionFields, isPartialLink,
-        cache, *dimensionKeySet);
+        parameters, mAllConditions, isPartialLink,
+        cache);
     return cache[index];
 }
 
-ConditionState ConditionWizard::getMetConditionDimension(
-        const int index, const vector<Matcher>& dimensionFields,
-        const bool isSubOutputDimensionFields,
-        std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const {
-    return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields,
-                                                           isSubOutputDimensionFields,
-                                                           *dimensionsKeySet);
-}
-
 const set<HashableDimensionKey>* ConditionWizard::getChangedToTrueDimensions(
         const int index) const {
     return mAllConditions[index]->getChangedToTrueDimensions(mAllConditions);
@@ -82,4 +70,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 2c88147..8926479 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -40,15 +40,7 @@
     // The ConditionTracker at [conditionIndex] can be a CombinationConditionTracker. In this case,
     // the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
     virtual ConditionState query(const int conditionIndex, const ConditionKey& conditionParameters,
-                                 const vector<Matcher>& dimensionFields,
-                                 const bool isSubOutputDimensionFields,
-                                 const bool isPartialLink,
-                                 std::unordered_set<HashableDimensionKey>* dimensionKeySet);
-
-    virtual ConditionState getMetConditionDimension(
-            const int index, const vector<Matcher>& dimensionFields,
-            const bool isSubOutputDimensionFields,
-            std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const;
+                                 const bool isPartialLink);
 
     virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(const int index) const;
     virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 87104a3..0c92149 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -344,11 +344,8 @@
 
 void SimpleConditionTracker::isConditionMet(
         const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
-        const vector<Matcher>& dimensionFields,
-        const bool isSubOutputDimensionFields,
         const bool isPartialLink,
-        vector<ConditionState>& conditionCache,
-        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
+        vector<ConditionState>& conditionCache) const {
 
     if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
         // it has been evaluated.
@@ -360,18 +357,13 @@
 
     if (pair == conditionParameters.end()) {
         ConditionState conditionState = ConditionState::kNotEvaluated;
-        if (dimensionFields.size() > 0 && dimensionFields[0].mMatcher.getTag() == mDimensionTag) {
-            conditionState = conditionState | getMetConditionDimension(
-                allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet);
-        } else {
-            conditionState = conditionState | mInitialValue;
-            if (!mSliced) {
-                const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
-                if (itr != mSlicedConditionState.end()) {
-                    ConditionState sliceState =
-                        itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
-                    conditionState = conditionState | sliceState;
-                }
+        conditionState = conditionState | mInitialValue;
+        if (!mSliced) {
+            const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
+            if (itr != mSlicedConditionState.end()) {
+                ConditionState sliceState =
+                    itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+                conditionState = conditionState | sliceState;
             }
         }
         conditionCache[mIndex] = conditionState;
@@ -389,15 +381,6 @@
                 slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
             if (slice.first.contains(key)) {
                 conditionState = conditionState | sliceState;
-                if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
-                    if (isSubOutputDimensionFields) {
-                        HashableDimensionKey dimensionKey;
-                        filterValues(dimensionFields, slice.first.getValues(), &dimensionKey);
-                        dimensionsKeySet.insert(dimensionKey);
-                    } else {
-                        dimensionsKeySet.insert(slice.first);
-                    }
-                }
             }
         }
     } else {
@@ -407,15 +390,6 @@
             ConditionState sliceState =
                 startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
             conditionState = conditionState | sliceState;
-            if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
-                if (isSubOutputDimensionFields) {
-                    HashableDimensionKey dimensionKey;
-                    filterValues(dimensionFields, startedCountIt->first.getValues(), &dimensionKey);
-                    dimensionsKeySet.insert(dimensionKey);
-                } else {
-                    dimensionsKeySet.insert(startedCountIt->first);
-                }
-            }
         }
 
     }
@@ -423,41 +397,6 @@
     VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
 }
 
-ConditionState SimpleConditionTracker::getMetConditionDimension(
-        const std::vector<sp<ConditionTracker>>& allConditions,
-        const vector<Matcher>& dimensionFields,
-        const bool isSubOutputDimensionFields,
-        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
-    ConditionState conditionState = mInitialValue;
-    if (dimensionFields.size() == 0 || mOutputDimensions.size() == 0 ||
-        dimensionFields[0].mMatcher.getTag() != mOutputDimensions[0].mMatcher.getTag()) {
-        const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
-        if (itr != mSlicedConditionState.end()) {
-            ConditionState sliceState =
-                itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
-            conditionState = conditionState | sliceState;
-        }
-        return conditionState;
-    }
-
-    for (const auto& slice : mSlicedConditionState) {
-        ConditionState sliceState =
-            slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
-        conditionState = conditionState | sliceState;
-
-        if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
-            if (isSubOutputDimensionFields) {
-                HashableDimensionKey dimensionKey;
-                filterValues(dimensionFields, slice.first.getValues(), &dimensionKey);
-                dimensionsKeySet.insert(dimensionKey);
-            } else {
-                dimensionsKeySet.insert(slice.first);
-            }
-        }
-    }
-    return conditionState;
-}
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 47d1ece..5c5cc56 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -48,17 +48,8 @@
 
     void isConditionMet(const ConditionKey& conditionParameters,
                         const std::vector<sp<ConditionTracker>>& allConditions,
-                        const vector<Matcher>& dimensionFields,
-                        const bool isSubOutputDimensionFields,
                         const bool isPartialLink,
-                        std::vector<ConditionState>& conditionCache,
-                        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
-
-    ConditionState getMetConditionDimension(
-            const std::vector<sp<ConditionTracker>>& allConditions,
-            const vector<Matcher>& dimensionFields,
-            const bool isSubOutputDimensionFields,
-            std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+                        std::vector<ConditionState>& conditionCache) const override;
 
     virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
             const std::vector<sp<ConditionTracker>>& allConditions) const {
diff --git a/cmds/statsd/src/condition/StateTracker.cpp b/cmds/statsd/src/condition/StateTracker.cpp
index 1965ce6..18c7178 100644
--- a/cmds/statsd/src/condition/StateTracker.cpp
+++ b/cmds/statsd/src/condition/StateTracker.cpp
@@ -178,11 +178,8 @@
 
 void StateTracker::isConditionMet(
         const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
-        const vector<Matcher>& dimensionFields,
-        const bool isSubOutputDimensionFields,
         const bool isPartialLink,
-        vector<ConditionState>& conditionCache,
-        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
+        vector<ConditionState>& conditionCache) const {
     if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
         // it has been evaluated.
         VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]);
@@ -193,10 +190,6 @@
     if (pair == conditionParameters.end()) {
         if (mSlicedState.size() > 0) {
             conditionCache[mIndex] = ConditionState::kTrue;
-
-            for (const auto& state : mSlicedState) {
-                dimensionsKeySet.insert(state.second);
-            }
         } else {
             conditionCache[mIndex] = ConditionState::kUnknown;
         }
@@ -208,25 +201,9 @@
     auto it = mSlicedState.find(primaryKey);
     if (it != mSlicedState.end()) {
         conditionCache[mIndex] = ConditionState::kTrue;
-        dimensionsKeySet.insert(it->second);
     }
 }
 
-ConditionState StateTracker::getMetConditionDimension(
-        const std::vector<sp<ConditionTracker>>& allConditions,
-        const vector<Matcher>& dimensionFields,
-        const bool isSubOutputDimensionFields,
-        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
-    if (mSlicedState.size() > 0) {
-        for (const auto& state : mSlicedState) {
-            dimensionsKeySet.insert(state.second);
-        }
-        return ConditionState::kTrue;
-    }
-
-    return mInitialValue;
-}
-
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/src/condition/StateTracker.h b/cmds/statsd/src/condition/StateTracker.h
index 2bdf98c..5ae4441 100644
--- a/cmds/statsd/src/condition/StateTracker.h
+++ b/cmds/statsd/src/condition/StateTracker.h
@@ -55,22 +55,8 @@
      */
     void isConditionMet(const ConditionKey& conditionParameters,
                         const std::vector<sp<ConditionTracker>>& allConditions,
-                        const vector<Matcher>& dimensionFields,
-                        const bool isSubOutputDimensionFields,
                         const bool isPartialLink,
-                        std::vector<ConditionState>& conditionCache,
-                        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
-
-    /**
-     * Note: dimensionFields will be ignored in StateTracker, because we demand metrics
-     * must take the entire dimension fields from StateTracker. This is to make implementation
-     * simple and efficient.
-     */
-    ConditionState getMetConditionDimension(
-            const std::vector<sp<ConditionTracker>>& allConditions,
-            const vector<Matcher>& dimensionFields,
-            const bool isSubOutputDimensionFields,
-            std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+                        std::vector<ConditionState>& conditionCache) const override;
 
     virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
             const std::vector<sp<ConditionTracker>>& allConditions) const {
@@ -128,4 +114,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index c023e6f..23d025f 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -48,7 +48,6 @@
 const int FIELD_ID_TIME_BASE = 9;
 const int FIELD_ID_BUCKET_SIZE = 10;
 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
 const int FIELD_ID_IS_ACTIVE = 14;
 
 // for CountMetricDataWrapper
@@ -82,12 +81,7 @@
         mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
     }
 
-    mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
-            HasPositionALL(metric.dimensions_in_condition());
-
-    if (metric.has_dimensions_in_condition()) {
-        translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
-    }
+    mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
 
     if (metric.links().size() > 0) {
         for (const auto& link : metric.links()) {
@@ -100,8 +94,6 @@
         mConditionSliced = true;
     }
 
-    mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
-
     flushIfNeededLocked(startTimeNs);
     // Adjust start for partial bucket
     mCurrentBucketStartTimeNs = startTimeNs;
@@ -171,13 +163,6 @@
             writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
             protoOutput->end(dimenPathToken);
         }
-        if (!mDimensionsInCondition.empty()) {
-            uint64_t dimenPathToken = protoOutput->start(
-                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
-            writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
-            protoOutput->end(dimenPathToken);
-        }
-
     }
 
     uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 96fbf7f..cca793b 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -47,7 +47,6 @@
 const int FIELD_ID_TIME_BASE = 9;
 const int FIELD_ID_BUCKET_SIZE = 10;
 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
 const int FIELD_ID_IS_ACTIVE = 14;
 // for DurationMetricDataWrapper
 const int FIELD_ID_DATA = 1;
@@ -100,12 +99,7 @@
         ALOGE("Position ANY in dimension_in_what not supported.");
     }
 
-    if (metric.has_dimensions_in_condition()) {
-        translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
-    }
-
-    mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
-            HasPositionALL(metric.dimensions_in_condition());
+    mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
 
     if (metric.links().size() > 0) {
         for (const auto& link : metric.links()) {
@@ -115,19 +109,16 @@
             translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
             mMetric2ConditionLinks.push_back(mc);
         }
+        mConditionSliced = true;
     }
-    mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
     mUnSlicedPartCondition = ConditionState::kUnknown;
 
     mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions);
-    if (mWizard != nullptr && mConditionTrackerIndex >= 0) {
-        mSameConditionDimensionsInTracker =
-            mWizard->equalOutputDimensions(mConditionTrackerIndex, mDimensionsInCondition);
-        if (mMetric2ConditionLinks.size() == 1) {
-            mHasLinksToAllConditionDimensionsInTracker =
-                mWizard->equalOutputDimensions(mConditionTrackerIndex,
-                                               mMetric2ConditionLinks.begin()->conditionFields);
-        }
+    if (mWizard != nullptr && mConditionTrackerIndex >= 0 &&
+            mMetric2ConditionLinks.size() == 1) {
+        mHasLinksToAllConditionDimensionsInTracker =
+            mWizard->equalOutputDimensions(mConditionTrackerIndex,
+                                           mMetric2ConditionLinks.begin()->conditionFields);
     }
     flushIfNeededLocked(startTimeNs);
     // Adjust start for partial bucket
@@ -164,13 +155,13 @@
         case DurationMetric_AggregationType_SUM:
             return make_unique<OringDurationTracker>(
                     mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
-                    mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
+                    mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
                     mTimeBaseNs, mBucketSizeNs, mConditionSliced,
                     mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
         case DurationMetric_AggregationType_MAX_SPARSE:
             return make_unique<MaxDurationTracker>(
                     mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
-                    mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
+                    mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
                     mTimeBaseNs, mBucketSizeNs, mConditionSliced,
                     mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
     }
@@ -178,13 +169,11 @@
 
 // SlicedConditionChange optimization case 1:
 // 1. If combination condition, logical operation is AND, only one sliced child predicate.
-// 2. No condition in dimension
-// 3. The links covers all dimension fields in the sliced child condition predicate.
+// 2. The links covers all dimension fields in the sliced child condition predicate.
 void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool condition,
                                                                    const int64_t eventTime) {
     if (mMetric2ConditionLinks.size() != 1 ||
-        !mHasLinksToAllConditionDimensionsInTracker ||
-        !mDimensionsInCondition.empty()) {
+        !mHasLinksToAllConditionDimensionsInTracker) {
         return;
     }
 
@@ -249,178 +238,20 @@
     }
 }
 
-
-// SlicedConditionChange optimization case 2:
-// 1. If combination condition, logical operation is AND, only one sliced child predicate.
-// 2. Has dimensions_in_condition and it equals to the output dimensions of the sliced predicate.
-void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt2(bool condition,
-                                                                   const int64_t eventTime) {
-    if (mMetric2ConditionLinks.size() > 1 || !mSameConditionDimensionsInTracker) {
-        return;
-    }
-
-    auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex);
-    auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex);
-
-    bool  currentUnSlicedPartCondition = true;
-    if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) {
-        ConditionState unslicedPartState =
-            mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex);
-        // When the unsliced part is still false, return directly.
-        if (mUnSlicedPartCondition == ConditionState::kFalse &&
-            unslicedPartState == ConditionState::kFalse) {
-            return;
-        }
-        mUnSlicedPartCondition = unslicedPartState;
-        currentUnSlicedPartCondition = mUnSlicedPartCondition > 0;
-    }
-
-    const std::set<HashableDimensionKey>* trueDimensionsToProcess = nullptr;
-    const std::set<HashableDimensionKey>* falseDimensionsToProcess = nullptr;
-
-    std::set<HashableDimensionKey> currentTrueConditionDimensions;
-    if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
-        (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
-        mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &currentTrueConditionDimensions);
-        trueDimensionsToProcess = &currentTrueConditionDimensions;
-    } else if (currentUnSlicedPartCondition) {
-        // Handles the condition change from the sliced predicate. If the unsliced condition state
-        // is not true, not need to do anything.
-        trueDimensionsToProcess = dimensionsChangedToTrue;
-        falseDimensionsToProcess = dimensionsChangedToFalse;
-    }
-
-    if (trueDimensionsToProcess == nullptr && falseDimensionsToProcess == nullptr) {
-        return;
-    }
-
-    for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
-        if (falseDimensionsToProcess != nullptr) {
-            for (const auto& changedDim : *falseDimensionsToProcess) {
-                auto condIt = whatIt.second.find(changedDim);
-                if (condIt != whatIt.second.end()) {
-                    condIt->second->onConditionChanged(false, eventTime);
-                }
-            }
-        }
-        if (trueDimensionsToProcess != nullptr) {
-            HashableDimensionKey linkedConditionDimensionKey;
-            if (!trueDimensionsToProcess->empty() && mMetric2ConditionLinks.size() == 1) {
-                getDimensionForCondition(whatIt.first.getValues(),
-                                         mMetric2ConditionLinks[0],
-                                         &linkedConditionDimensionKey);
-            }
-            for (auto& trueDim : *trueDimensionsToProcess) {
-                auto condIt = whatIt.second.find(trueDim);
-                if (condIt != whatIt.second.end()) {
-                    condIt->second->onConditionChanged(
-                            currentUnSlicedPartCondition, eventTime);
-                } else {
-                    if (mMetric2ConditionLinks.size() == 0 ||
-                        trueDim.contains(linkedConditionDimensionKey)) {
-                        if (!whatIt.second.empty()) {
-                            auto newEventKey = MetricDimensionKey(whatIt.first, trueDim);
-                            if (hitGuardRailLocked(newEventKey)) {
-                                continue;
-                            }
-                            unique_ptr<DurationTracker> newTracker =
-                                whatIt.second.begin()->second->clone(eventTime);
-                            if (newTracker != nullptr) {
-                                newTracker->setEventKey(newEventKey);
-                                newTracker->onConditionChanged(true, eventTime);
-                                whatIt.second[trueDim] = std::move(newTracker);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
 void DurationMetricProducer::onSlicedConditionMayChangeInternalLocked(bool overallCondition,
         const int64_t eventTimeNs) {
     bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex);
-    if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker &&
-        mDimensionsInCondition.empty()) {
+    if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker) {
         onSlicedConditionMayChangeLocked_opt1(overallCondition, eventTimeNs);
         return;
     }
 
-    if (changeDimTrackable && mSameConditionDimensionsInTracker &&
-        mMetric2ConditionLinks.size() <= 1) {
-        onSlicedConditionMayChangeLocked_opt2(overallCondition, eventTimeNs);
-        return;
-    }
-
     // Now for each of the on-going event, check if the condition has changed for them.
     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
         for (auto& pair : whatIt.second) {
             pair.second->onSlicedConditionMayChange(overallCondition, eventTimeNs);
         }
     }
-
-    if (mDimensionsInCondition.empty()) {
-        return;
-    }
-
-    if (mMetric2ConditionLinks.empty()) {
-        std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
-        mWizard->getMetConditionDimension(mConditionTrackerIndex, mDimensionsInCondition,
-                                          !mSameConditionDimensionsInTracker,
-                                          &conditionDimensionsKeySet);
-        for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
-            for (const auto& pair : whatIt.second) {
-                conditionDimensionsKeySet.erase(pair.first);
-            }
-        }
-        for (const auto& conditionDimension : conditionDimensionsKeySet) {
-            for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
-                if (!whatIt.second.empty()) {
-                    auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension);
-                    if (hitGuardRailLocked(newEventKey)) {
-                        continue;
-                    }
-                    unique_ptr<DurationTracker> newTracker =
-                        whatIt.second.begin()->second->clone(eventTimeNs);
-                    if (newTracker != nullptr) {
-                        newTracker->setEventKey(MetricDimensionKey(newEventKey));
-                        newTracker->onSlicedConditionMayChange(overallCondition, eventTimeNs);
-                        whatIt.second[conditionDimension] = std::move(newTracker);
-                    }
-                }
-            }
-        }
-    } else {
-        for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
-            ConditionKey conditionKey;
-            for (const auto& link : mMetric2ConditionLinks) {
-                getDimensionForCondition(whatIt.first.getValues(), link,
-                                         &conditionKey[link.conditionId]);
-            }
-            std::unordered_set<HashableDimensionKey> conditionDimensionsKeys;
-            mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
-                           !mSameConditionDimensionsInTracker,
-                           !mHasLinksToAllConditionDimensionsInTracker,
-                           &conditionDimensionsKeys);
-
-            for (const auto& conditionDimension : conditionDimensionsKeys) {
-                if (!whatIt.second.empty() &&
-                    whatIt.second.find(conditionDimension) == whatIt.second.end()) {
-                    auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension);
-                    if (hitGuardRailLocked(newEventKey)) {
-                        continue;
-                    }
-                    auto newTracker = whatIt.second.begin()->second->clone(eventTimeNs);
-                    if (newTracker != nullptr) {
-                        newTracker->setEventKey(newEventKey);
-                        newTracker->onSlicedConditionMayChange(overallCondition, eventTimeNs);
-                        whatIt.second[conditionDimension] = std::move(newTracker);
-                    }
-                }
-            }
-        }
-    }
 }
 
 void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
@@ -526,12 +357,6 @@
             writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
             protoOutput->end(dimenPathToken);
         }
-        if (!mDimensionsInCondition.empty()) {
-            uint64_t dimenPathToken = protoOutput->start(
-                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
-            writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
-            protoOutput->end(dimenPathToken);
-        }
     }
 
     uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
@@ -790,52 +615,24 @@
 
     bool condition;
     ConditionKey conditionKey;
-    std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
     if (mConditionSliced) {
         for (const auto& link : mMetric2ConditionLinks) {
             getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
         }
 
         auto conditionState =
-            mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
-                           !mSameConditionDimensionsInTracker,
-                           !mHasLinksToAllConditionDimensionsInTracker,
-                           &dimensionKeysInCondition);
+            mWizard->query(mConditionTrackerIndex, conditionKey,
+                           !mHasLinksToAllConditionDimensionsInTracker);
         condition = conditionState == ConditionState::kTrue;
-        if (mDimensionsInCondition.empty() && condition) {
-            dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
-        }
     } else {
         // TODO: The unknown condition state is not handled here, we should fix it.
         condition = mCondition == ConditionState::kTrue;
-        if (condition) {
-            dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
-        }
     }
 
     condition = condition && mIsActive;
 
-    if (dimensionKeysInCondition.empty()) {
-        handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY),
-                         conditionKey, condition, event);
-    } else {
-        auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
-        // If the what dimension is already there, we should update all the trackers even
-        // the condition is false.
-        if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
-            for (const auto& condIt : whatIt->second) {
-                const bool cond = dimensionKeysInCondition.find(condIt.first) !=
-                        dimensionKeysInCondition.end() && condition;
-                handleStartEvent(MetricDimensionKey(dimensionInWhat, condIt.first),
-                    conditionKey, cond, event);
-                dimensionKeysInCondition.erase(condIt.first);
-            }
-        }
-        for (const auto& conditionDimension : dimensionKeysInCondition) {
-            handleStartEvent(MetricDimensionKey(dimensionInWhat, conditionDimension), conditionKey,
-                             condition, event);
-        }
-    }
+    handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY),
+                     conditionKey, condition, event);
 }
 
 size_t DurationMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index a64bbc1..1f423cd 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -48,7 +48,6 @@
 const int FIELD_ID_TIME_BASE = 9;
 const int FIELD_ID_BUCKET_SIZE = 10;
 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
 const int FIELD_ID_IS_ACTIVE = 14;
 // for GaugeMetricDataWrapper
 const int FIELD_ID_DATA = 1;
@@ -115,10 +114,6 @@
         mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
     }
 
-    if (metric.has_dimensions_in_condition()) {
-        translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
-    }
-
     if (metric.links().size() > 0) {
         for (const auto& link : metric.links()) {
             Metric2Condition mc;
@@ -127,10 +122,9 @@
             translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
             mMetric2ConditionLinks.push_back(mc);
         }
+        mConditionSliced = true;
     }
-    mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
-    mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
-            HasPositionALL(metric.dimensions_in_condition());
+    mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
 
     flushIfNeededLocked(startTimeNs);
     // Kicks off the puller immediately.
@@ -209,12 +203,6 @@
             writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
             protoOutput->end(dimenPathToken);
         }
-        if (!mDimensionsInCondition.empty()) {
-            uint64_t dimenPathToken = protoOutput->start(
-                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
-            writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
-            protoOutput->end(dimenPathToken);
-        }
     }
 
     uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 92752b2..1ab4fdf 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -52,38 +52,24 @@
 
     bool condition;
     ConditionKey conditionKey;
-    std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
     if (mConditionSliced) {
         for (const auto& link : mMetric2ConditionLinks) {
             getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
         }
         auto conditionState =
-            mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
-                           !mSameConditionDimensionsInTracker,
-                           !mHasLinksToAllConditionDimensionsInTracker,
-                           &dimensionKeysInCondition);
+            mWizard->query(mConditionTrackerIndex, conditionKey,
+                           !mHasLinksToAllConditionDimensionsInTracker);
         condition = (conditionState == ConditionState::kTrue);
     } else {
         // TODO: The unknown condition state is not handled here, we should fix it.
         condition = mCondition == ConditionState::kTrue;
     }
 
-    if (mDimensionsInCondition.empty() && condition) {
-        dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
-    }
-
     HashableDimensionKey dimensionInWhat;
     filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
     MetricDimensionKey metricKey(dimensionInWhat, DEFAULT_DIMENSION_KEY);
-    for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
-        metricKey.setDimensionKeyInCondition(conditionDimensionKey);
-        onMatchedLogEventInternalLocked(
-                matcherIndex, metricKey, conditionKey, condition, event);
-    }
-    if (dimensionKeysInCondition.empty()) {
-        onMatchedLogEventInternalLocked(
-                matcherIndex, metricKey, conditionKey, condition, event);
-    }
+    onMatchedLogEventInternalLocked(
+            matcherIndex, metricKey, conditionKey, condition, event);
 }
 
 bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 09ad290..94f833b 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -88,7 +88,6 @@
           mConditionTrackerIndex(conditionIndex),
           mContainANYPositionInDimensionsInWhat(false),
           mSliceByPositionALL(false),
-          mSameConditionDimensionsInTracker(false),
           mHasLinksToAllConditionDimensionsInTracker(false),
           mIsActive(true) {
     }
@@ -349,15 +348,10 @@
     int mConditionTrackerIndex;
 
     vector<Matcher> mDimensionsInWhat;       // The dimensions_in_what defined in statsd_config
-    vector<Matcher> mDimensionsInCondition;  // The dimensions_in_condition defined in statsd_config
 
     bool mContainANYPositionInDimensionsInWhat;
     bool mSliceByPositionALL;
 
-    // True iff the condition dimensions equal to the sliced dimensions in the simple condition
-    // tracker. This field is always false for combinational condition trackers.
-    bool mSameConditionDimensionsInTracker;
-
     // True iff the metric to condition links cover all dimension fields in the condition tracker.
     // This field is always false for combinational condition trackers.
     bool mHasLinksToAllConditionDimensionsInTracker;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 8efca1e..3dad614 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -260,17 +260,6 @@
     FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
     FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
     FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
-    FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
-    FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
-    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition);
-    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition);
-
-    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition);
-    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition);
-    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition);
-    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition);
-    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition);
-    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition);
 
     FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
     FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 0e33a0f..bc16024 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -51,7 +51,6 @@
 const int FIELD_ID_TIME_BASE = 9;
 const int FIELD_ID_BUCKET_SIZE = 10;
 const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
 const int FIELD_ID_IS_ACTIVE = 14;
 // for ValueMetricDataWrapper
 const int FIELD_ID_DATA = 1;
@@ -127,10 +126,6 @@
         mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
     }
 
-    if (metric.has_dimensions_in_condition()) {
-        translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
-    }
-
     if (metric.links().size() > 0) {
         for (const auto& link : metric.links()) {
             Metric2Condition mc;
@@ -139,11 +134,10 @@
             translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
             mMetric2ConditionLinks.push_back(mc);
         }
+        mConditionSliced = true;
     }
 
-    mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
-    mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
-                          HasPositionALL(metric.dimensions_in_condition());
+    mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
 
     int64_t numBucketsForward = calcBucketsForwardCount(startTimeNs);
     mCurrentBucketNum += numBucketsForward;
@@ -243,12 +237,6 @@
             writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
             protoOutput->end(dimenPathToken);
         }
-        if (!mDimensionsInCondition.empty()) {
-            uint64_t dimenPathToken =
-                    protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
-            writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
-            protoOutput->end(dimenPathToken);
-        }
     }
 
     uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 081e61e..6b5c299 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -60,7 +60,7 @@
 public:
     DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
                     sp<ConditionWizard> wizard, int conditionIndex,
-                    const std::vector<Matcher>& dimensionInCondition, bool nesting,
+                    bool nesting,
                     int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
                     int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
                     const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
@@ -70,7 +70,6 @@
           mWizard(wizard),
           mConditionTrackerIndex(conditionIndex),
           mBucketSizeNs(bucketSizeNs),
-          mDimensionInCondition(dimensionInCondition),
           mNested(nesting),
           mCurrentBucketStartTimeNs(currentBucketStartNs),
           mDuration(0),
@@ -180,8 +179,6 @@
 
     const int64_t mBucketSizeNs;
 
-    const std::vector<Matcher>& mDimensionInCondition;
-
     const bool mNested;
 
     int64_t mCurrentBucketStartTimeNs;
@@ -196,7 +193,6 @@
 
     const bool mConditionSliced;
 
-    bool mSameConditionDimensionsInTracker;
     bool mHasLinksToAllConditionDimensionsInTracker;
 
     std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 6868b8c..df66cb0 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -27,18 +27,14 @@
 MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
                                        const MetricDimensionKey& eventKey,
                                        sp<ConditionWizard> wizard, int conditionIndex,
-                                       const vector<Matcher>& dimensionInCondition, bool nesting,
+                                       bool nesting,
                                        int64_t currentBucketStartNs, int64_t currentBucketNum,
                                        int64_t startTimeNs, int64_t bucketSizeNs,
                                        bool conditionSliced, bool fullLink,
                                        const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
-    : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
+    : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting,
                       currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs,
                       conditionSliced, fullLink, anomalyTrackers) {
-    if (mWizard != nullptr) {
-        mSameConditionDimensionsInTracker =
-            mWizard->equalOutputDimensions(conditionIndex, mDimensionInCondition);
-    }
 }
 
 unique_ptr<DurationTracker> MaxDurationTracker::clone(const int64_t eventTime) {
@@ -252,17 +248,11 @@
         if (pair.second.state == kStopped) {
             continue;
         }
-        std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
         ConditionState conditionState = mWizard->query(
-            mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition,
-            !mSameConditionDimensionsInTracker,
-            !mHasLinksToAllConditionDimensionsInTracker,
-            &conditionDimensionKeySet);
-        bool conditionMet =
-                (conditionState == ConditionState::kTrue) &&
-                (mDimensionInCondition.size() == 0 ||
-                 conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
-                         conditionDimensionKeySet.end());
+            mConditionTrackerIndex, pair.second.conditionKeys,
+            !mHasLinksToAllConditionDimensionsInTracker);
+        bool conditionMet = (conditionState == ConditionState::kTrue);
+
         VLOG("key: %s, condition: %d", pair.first.toString().c_str(), conditionMet);
         noteConditionChanged(pair.first, conditionMet, timestamp);
     }
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 8e8f2cd..d0371da 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -30,7 +30,7 @@
 public:
     MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
                        sp<ConditionWizard> wizard, int conditionIndex,
-                       const std::vector<Matcher>& dimensionInCondition, bool nesting,
+                       bool nesting,
                        int64_t currentBucketStartNs, int64_t currentBucketNum,
                        int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
                        bool fullLink,
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index 956383a..b0fd975 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -26,20 +26,16 @@
 
 OringDurationTracker::OringDurationTracker(
         const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
-        sp<ConditionWizard> wizard, int conditionIndex, const vector<Matcher>& dimensionInCondition,
+        sp<ConditionWizard> wizard, int conditionIndex,
         bool nesting, int64_t currentBucketStartNs, int64_t currentBucketNum,
         int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
         const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
-    : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
+    : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting,
                       currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs,
                       conditionSliced, fullLink, anomalyTrackers),
       mStarted(),
       mPaused() {
     mLastStartTime = 0;
-    if (mWizard != nullptr) {
-        mSameConditionDimensionsInTracker =
-            mWizard->equalOutputDimensions(conditionIndex, mDimensionInCondition);
-    }
 }
 
 unique_ptr<DurationTracker> OringDurationTracker::clone(const int64_t eventTime) {
@@ -227,17 +223,10 @@
                 ++it;
                 continue;
             }
-            std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
             ConditionState conditionState =
                 mWizard->query(mConditionTrackerIndex, condIt->second,
-                               mDimensionInCondition,
-                               !mSameConditionDimensionsInTracker,
-                               !mHasLinksToAllConditionDimensionsInTracker,
-                               &conditionDimensionKeySet);
-            if (conditionState != ConditionState::kTrue ||
-                (mDimensionInCondition.size() != 0 &&
-                 conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) ==
-                         conditionDimensionKeySet.end())) {
+                               !mHasLinksToAllConditionDimensionsInTracker);
+            if (conditionState != ConditionState::kTrue) {
                 startedToPaused.push_back(*it);
                 it = mStarted.erase(it);
                 VLOG("Key %s started -> paused", key.toString().c_str());
@@ -262,17 +251,10 @@
                 ++it;
                 continue;
             }
-            std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
             ConditionState conditionState =
                 mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
-                               mDimensionInCondition,
-                               !mSameConditionDimensionsInTracker,
-                               !mHasLinksToAllConditionDimensionsInTracker,
-                               &conditionDimensionKeySet);
-            if (conditionState == ConditionState::kTrue &&
-                (mDimensionInCondition.size() == 0 ||
-                 conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
-                         conditionDimensionKeySet.end())) {
+                               !mHasLinksToAllConditionDimensionsInTracker);
+            if (conditionState == ConditionState::kTrue) {
                 pausedToStarted.push_back(*it);
                 it = mPaused.erase(it);
                 VLOG("Key %s paused -> started", key.toString().c_str());
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index 8e73256..43c48d5 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -29,7 +29,7 @@
 public:
     OringDurationTracker(const ConfigKey& key, const int64_t& id,
                          const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
-                         int conditionIndex, const std::vector<Matcher>& dimensionInCondition,
+                         int conditionIndex,
                          bool nesting, int64_t currentBucketStartNs, int64_t currentBucketNum,
                          int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
                          bool fullLink,
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index b875470..d9c04f2 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -66,13 +66,13 @@
 message CountMetricData {
   optional DimensionsValue dimensions_in_what = 1;
 
-  optional DimensionsValue dimensions_in_condition = 2;
+  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
 
   repeated CountBucketInfo bucket_info = 3;
 
   repeated DimensionsValue dimension_leaf_values_in_what = 4;
 
-  repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+  repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
 }
 
 message DurationBucketInfo {
@@ -92,13 +92,13 @@
 message DurationMetricData {
   optional DimensionsValue dimensions_in_what = 1;
 
-  optional DimensionsValue dimensions_in_condition = 2;
+  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
 
   repeated DurationBucketInfo bucket_info = 3;
 
   repeated DimensionsValue dimension_leaf_values_in_what = 4;
 
-  repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+  repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
 }
 
 message ValueBucketInfo {
@@ -136,13 +136,13 @@
 message ValueMetricData {
   optional DimensionsValue dimensions_in_what = 1;
 
-  optional DimensionsValue dimensions_in_condition = 2;
+  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
 
   repeated ValueBucketInfo bucket_info = 3;
 
   repeated DimensionsValue dimension_leaf_values_in_what = 4;
 
-  repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+  repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
 }
 
 message GaugeBucketInfo {
@@ -166,13 +166,13 @@
 message GaugeMetricData {
   optional DimensionsValue dimensions_in_what = 1;
 
-  optional DimensionsValue dimensions_in_condition = 2;
+  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
 
   repeated GaugeBucketInfo bucket_info = 3;
 
   repeated DimensionsValue dimension_leaf_values_in_what = 4;
 
-  repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+  repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
 }
 
 message StatsLogReport {
@@ -220,7 +220,7 @@
 
   optional DimensionsValue dimensions_path_in_what = 11;
 
-  optional DimensionsValue dimensions_path_in_condition = 12;
+  optional DimensionsValue dimensions_path_in_condition = 12 [deprecated = true];
 
   // DO NOT USE field 13.
 
@@ -363,7 +363,7 @@
         repeated ConditionStats condition_stats = 14;
         repeated MetricStats metric_stats = 15;
         repeated AlertStats alert_stats = 16;
-        repeated MetricStats metric_dimension_in_condition_stats = 17;
+        repeated MetricStats metric_dimension_in_condition_stats = 17 [deprecated = true];
         message Annotation {
             optional int64 field_int64 = 1;
             optional int32 field_int32 = 2;
@@ -483,7 +483,7 @@
     message MetricValue {
         optional int64 metric_id = 1;
         optional DimensionsValue dimension_in_what = 2;
-        optional DimensionsValue dimension_in_condition = 3;
+        optional DimensionsValue dimension_in_condition = 3 [deprecated = true];
         optional int64 value = 4;
     }
     oneof value {
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 79c06b9..1b7f398 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -182,7 +182,7 @@
 
   optional FieldMatcher dimensions_in_what = 4;
 
-  optional FieldMatcher dimensions_in_condition = 7;
+  optional FieldMatcher dimensions_in_condition = 7 [deprecated = true];
 
   optional TimeUnit bucket = 5;
 
@@ -207,7 +207,7 @@
 
   optional FieldMatcher dimensions_in_what = 6;
 
-  optional FieldMatcher dimensions_in_condition = 8;
+  optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
 
   optional TimeUnit bucket = 7;
 }
@@ -225,7 +225,7 @@
 
   optional FieldMatcher dimensions_in_what = 5;
 
-  optional FieldMatcher dimensions_in_condition = 8;
+  optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
 
   optional TimeUnit bucket = 6;
 
@@ -259,7 +259,7 @@
 
   optional FieldMatcher dimensions_in_what = 5;
 
-  optional FieldMatcher dimensions_in_condition = 9;
+  optional FieldMatcher dimensions_in_condition = 9 [deprecated = true];
 
   optional TimeUnit bucket = 6;
 
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index e826a52..6eaa231 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -268,8 +268,6 @@
     std::vector<sp<ConditionTracker>> allConditions;
     for (Position position :
             { Position::FIRST, Position::LAST}) {
-        vector<Matcher> dimensionInCondition;
-        std::unordered_set<HashableDimensionKey> dimensionKeys;
 
         SimplePredicate simplePredicate = getWakeLockHeldCondition(
                 true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
@@ -321,9 +319,9 @@
         const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
 
-        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
-                                        false, false,
-                                        conditionCache, dimensionKeys);
+        conditionTracker.isConditionMet(queryKey, allPredicates,
+                                        false,
+                                        conditionCache);
         EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
         // another wake lock acquired by this uid
@@ -389,9 +387,9 @@
 
         // query again
         conditionCache[0] = ConditionState::kNotEvaluated;
-        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
-                                        false, false,
-                                        conditionCache, dimensionKeys);
+        conditionTracker.isConditionMet(queryKey, allPredicates,
+                                        false,
+                                        conditionCache);
         EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
     }
 
@@ -399,8 +397,6 @@
 
 TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
     std::vector<sp<ConditionTracker>> allConditions;
-    vector<Matcher> dimensionInCondition;
-    std::unordered_set<HashableDimensionKey> dimensionKeys;
 
     SimplePredicate simplePredicate = getWakeLockHeldCondition(
             true /*nesting*/, true /*default to false*/, false /*slice output by uid*/,
@@ -445,9 +441,9 @@
     ConditionKey queryKey;
     conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
-                                    true, true,
-                                    conditionCache, dimensionKeys);
+    conditionTracker.isConditionMet(queryKey, allPredicates,
+                                    true,
+                                    conditionCache);
     EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
     // another wake lock acquired by this uid
@@ -489,10 +485,9 @@
 
     // query again
     conditionCache[0] = ConditionState::kNotEvaluated;
-    dimensionKeys.clear();
-    conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
-                                    true, true,
-                                    conditionCache, dimensionKeys);
+    conditionTracker.isConditionMet(queryKey, allPredicates,
+                                    true,
+                                    conditionCache);
     EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
 }
 
@@ -500,8 +495,6 @@
     std::vector<sp<ConditionTracker>> allConditions;
     for (Position position :
             { Position::FIRST, Position::LAST }) {
-        vector<Matcher> dimensionInCondition;
-        std::unordered_set<HashableDimensionKey> dimensionKeys;
         SimplePredicate simplePredicate = getWakeLockHeldCondition(
                 true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
                 position);
@@ -555,9 +548,9 @@
         const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
 
-        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
-                                        false, false,
-                                        conditionCache, dimensionKeys);
+        conditionTracker.isConditionMet(queryKey, allPredicates,
+                                        false,
+                                        conditionCache);
         EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
         // another wake lock acquired by uid2
@@ -594,9 +587,9 @@
         // TEST QUERY
         const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
-        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
-                                        false, false,
-                                        conditionCache, dimensionKeys);
+        conditionTracker.isConditionMet(queryKey, allPredicates,
+                                        false,
+                                        conditionCache);
 
         EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
@@ -628,17 +621,17 @@
         // TEST QUERY
         const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
-        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
-                                        false, false,
-                                        conditionCache, dimensionKeys);
+        conditionTracker.isConditionMet(queryKey, allPredicates,
+                                        false,
+                                        conditionCache);
         EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
 
         // TEST QUERY
         const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
-        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
-                                        false, false,
-                                        conditionCache, dimensionKeys);
+        conditionTracker.isConditionMet(queryKey, allPredicates,
+                                        false,
+                                        conditionCache);
         EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
     }
 }
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
deleted file mode 100644
index e4186b7..0000000
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
+++ /dev/null
@@ -1,961 +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.
-
-#include <gtest/gtest.h>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-StatsdConfig CreateDurationMetricConfig_NoLink_AND_CombinationCondition(
-        DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition,
-        bool hashStringInReport) {
-    StatsdConfig config;
-    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-    *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
-    *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
-    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-
-    auto scheduledJobPredicate = CreateScheduledJobPredicate();
-    auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
-    dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
-    dimensions->add_child()->set_field(2);  // job name field.
-
-    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
-
-    auto isSyncingPredicate = CreateIsSyncingPredicate();
-    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
-                                                          {Position::FIRST});
-    if (addExtraDimensionInCondition) {
-        syncDimension->add_child()->set_field(2 /* name field*/);
-    }
-
-    config.set_hash_strings_in_metric_report(hashStringInReport);
-    *config.add_predicate() = scheduledJobPredicate;
-    *config.add_predicate() = screenIsOffPredicate;
-    *config.add_predicate() = isSyncingPredicate;
-    auto combinationPredicate = config.add_predicate();
-    combinationPredicate->set_id(StringToId("CombinationPredicate"));
-    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
-    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
-    addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
-    auto metric = config.add_duration_metric();
-    metric->set_bucket(FIVE_MINUTES);
-    metric->set_id(StringToId("scheduledJob"));
-    metric->set_what(scheduledJobPredicate.id());
-    metric->set_condition(combinationPredicate->id());
-    metric->set_aggregation_type(aggregationType);
-    auto dimensionWhat = metric->mutable_dimensions_in_what();
-    dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
-    dimensionWhat->add_child()->set_field(2);  // job name field.
-    *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
-            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-    return config;
-}
-
-}  // namespace
-
-/*
- The following test has the following input.
-
-{ 10000000002 10000000002 (8)9999[I], [S], job0[S], 1[I],  }
-{ 10000000010 10000000010 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 1[I],  }
-{ 10000000011 10000000011 (29)1[I],  }
-{ 10000000040 10000000040 (29)2[I],  }
-{ 10000000050 10000000050 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 0[I],  }
-{ 10000000101 10000000101 (8)9999[I], [S], job0[S], 0[I],  }
-{ 10000000102 10000000102 (29)1[I],  }
-{ 10000000200 10000000200 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 1[I],  }
-{ 10000000201 10000000201 (8)9999[I], [S], job2[S], 1[I],  }
-{ 10000000400 10000000400 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadDoc[S], 1[I],  }
-{ 10000000401 10000000401 (7)333[I], App2[S], 222[I], GMSCoreModule1[S], 555[I], GMSCoreModule2[S], ReadEmail[S], 1[I],  }
-{ 10000000450 10000000450 (29)2[I],  }
-{ 10000000500 10000000500 (8)9999[I], [S], job2[S], 0[I],  }
-{ 10000000600 10000000600 (8)8888[I], [S], job2[S], 1[I],  }
-{ 10000000650 10000000650 (29)1[I],  }
-{ 309999999999 309999999999 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadDoc[S], 0[I],  }
-{ 310000000100 310000000100 (29)2[I],  }
-{ 310000000300 310000000300 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 0[I],  }
-{ 310000000600 310000000600 (8)8888[I], [S], job1[S], 1[I],  }
-{ 310000000640 310000000640 (29)1[I],  }
-{ 310000000650 310000000650 (29)2[I],  }
-{ 310000000700 310000000700 (7)333[I], App2[S], 222[I], GMSCoreModule1[S], 555[I], GMSCoreModule2[S], ReadEmail[S], 0[I],  }
-{ 310000000850 310000000850 (8)8888[I], [S], job2[S], 0[I],  }
-{ 310000000900 310000000900 (8)8888[I], [S], job1[S], 0[I],  }
-*/
-TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition) {
-    for (const bool hashStringInReport : { true, false }) {
-        for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : { true, false }) {
-            for (auto aggregationType : {DurationMetric::MAX_SPARSE, DurationMetric::SUM}) {
-                ConfigKey cfgKey;
-                auto config = CreateDurationMetricConfig_NoLink_AND_CombinationCondition(
-                        aggregationType, isDimensionInConditionSubSetOfConditionTrackerDimension,
-                        hashStringInReport);
-                int64_t bucketStartTimeNs = 10000000000;
-                int64_t bucketSizeNs =
-                        TimeUnitToBucketSizeInMillis(
-                            config.duration_metric(0).bucket()) * 1000000LL;
-
-                auto processor = CreateStatsLogProcessor(
-                        bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-                EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-                EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
-                std::vector<AttributionNodeInternal> attributions1 = {
-                        CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-                        CreateAttribution(222, "GMSCoreModule2")};
-
-                std::vector<AttributionNodeInternal> attributions2 = {
-                        CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-                        CreateAttribution(555, "GMSCoreModule2")};
-
-                std::vector<std::unique_ptr<LogEvent>> events;
-
-                events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                               bucketStartTimeNs + 11));
-                events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                               bucketStartTimeNs + 40));
-
-                events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                               bucketStartTimeNs + 102));
-                events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                               bucketStartTimeNs + 450));
-
-                events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                               bucketStartTimeNs + 650));
-                events.push_back(CreateScreenStateChangedEvent(
-                                    android::view::DISPLAY_STATE_ON,
-                                    bucketStartTimeNs + bucketSizeNs + 100));
-
-                events.push_back(CreateScreenStateChangedEvent(
-                                    android::view::DISPLAY_STATE_OFF,
-                                    bucketStartTimeNs + bucketSizeNs + 640));
-                events.push_back(CreateScreenStateChangedEvent(
-                                    android::view::DISPLAY_STATE_ON,
-                                    bucketStartTimeNs + bucketSizeNs + 650));
-
-                events.push_back(CreateStartScheduledJobEvent(
-                        {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 2));
-                events.push_back(CreateFinishScheduledJobEvent(
-                        {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101));
-
-                events.push_back(CreateStartScheduledJobEvent(
-                        {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201));
-                events.push_back(CreateFinishScheduledJobEvent(
-                        {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500));
-
-                events.push_back(CreateStartScheduledJobEvent(
-                        {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600));
-                events.push_back(CreateFinishScheduledJobEvent(
-                        {CreateAttribution(8888, "")}, "job2",
-                        bucketStartTimeNs + bucketSizeNs + 850));
-
-                events.push_back(CreateStartScheduledJobEvent(
-                        {CreateAttribution(8888, "")}, "job1",
-                        bucketStartTimeNs + bucketSizeNs + 600));
-                events.push_back(CreateFinishScheduledJobEvent(
-                        {CreateAttribution(8888, "")}, "job1",
-                        bucketStartTimeNs + bucketSizeNs + 900));
-
-                events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                                      bucketStartTimeNs + 10));
-                events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                                    bucketStartTimeNs + 50));
-
-                events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                                      bucketStartTimeNs + 200));
-                events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                                    bucketStartTimeNs + bucketSizeNs + 300));
-
-                events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc",
-                                                      bucketStartTimeNs + 400));
-                events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
-                                                    bucketStartTimeNs + bucketSizeNs - 1));
-
-                events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
-                                                      bucketStartTimeNs + 401));
-                events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-                                                    bucketStartTimeNs + bucketSizeNs + 700));
-
-                sortLogEventsByTimestamp(&events);
-
-                for (const auto& event : events) {
-                    processor->OnLogEvent(event.get());
-                }
-
-                ConfigMetricsReportList reports;
-                vector<uint8_t> buffer;
-                processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                        true, ADB_DUMP, FAST, &buffer);
-                EXPECT_TRUE(buffer.size() > 0);
-                EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-                backfillDimensionPath(&reports);
-                backfillStringInReport(&reports);
-                backfillStartEndTimestamp(&reports);
-
-                EXPECT_EQ(reports.reports_size(), 1);
-                EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-                StatsLogReport::DurationMetricDataWrapper metrics;
-                sortMetricDataByDimensionsValue(
-                        reports.reports(0).metrics(0).duration_metrics(), &metrics);
-                if (aggregationType == DurationMetric::SUM) {
-                    EXPECT_EQ(metrics.data_size(), 4);
-                    auto data = metrics.data(0);
-                    EXPECT_EQ(data.dimensions_in_what().field(),
-                              android::util::SCHEDULED_JOB_STATE_CHANGED);
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                              2);  // job name field
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().
-                                    dimensions_value(0).value_str(),
-                              "job0");  // job name
-                    ValidateAttributionUidAndTagDimension(
-                            data.dimensions_in_condition(),
-                            android::util::SYNC_STATE_CHANGED, 111, "App1");
-                    EXPECT_EQ(data.bucket_info_size(), 1);
-                    EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40 - 11);
-                    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                              bucketStartTimeNs);
-                    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                        bucketStartTimeNs + bucketSizeNs);
-
-
-                    data = metrics.data(1);
-                    EXPECT_EQ(data.dimensions_in_what().field(),
-                              android::util::SCHEDULED_JOB_STATE_CHANGED);
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                              2);  // job name field
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().
-                                    dimensions_value(0).value_str(),
-                              "job1");  // job name
-                    ValidateAttributionUidAndTagDimension(
-                            data.dimensions_in_condition(),
-                            android::util::SYNC_STATE_CHANGED, 333, "App2");
-                    EXPECT_EQ(data.bucket_info_size(), 1);
-                    EXPECT_EQ(data.bucket_info(0).duration_nanos(), 10);
-                    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + bucketSizeNs);
-                    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                        bucketStartTimeNs + 2 * bucketSizeNs);
-
-                    data = metrics.data(2);
-                    EXPECT_EQ(data.dimensions_in_what().field(),
-                              android::util::SCHEDULED_JOB_STATE_CHANGED);
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                              2);  // job name field
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().
-                                    dimensions_value(0).value_str(),
-                              "job2");  // job name
-                    ValidateAttributionUidAndTagDimension(
-                            data.dimensions_in_condition(),
-                            android::util::SYNC_STATE_CHANGED, 111, "App1");
-                    EXPECT_EQ(data.bucket_info_size(), 2);
-                    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + bucketSizeNs);
-                    EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201 + bucketSizeNs - 650);
-                    EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
-                    EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + bucketSizeNs);
-                    EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + 2 * bucketSizeNs);
-
-                    data = metrics.data(3);
-                    EXPECT_EQ(data.dimensions_in_what().field(),
-                              android::util::SCHEDULED_JOB_STATE_CHANGED);
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                              2);  // job name field
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().
-                                    dimensions_value(0).value_str(),
-                              "job2");  // job name
-                    ValidateAttributionUidAndTagDimension(
-                            data.dimensions_in_condition(),
-                            android::util::SYNC_STATE_CHANGED, 333, "App2");
-                    EXPECT_EQ(data.bucket_info_size(), 2);
-                    EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401 + bucketSizeNs - 650);
-                    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + bucketSizeNs);
-                    EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100 + 650 - 640);
-                    EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + bucketSizeNs);
-                    EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + 2 * bucketSizeNs);
-                } else {
-                    EXPECT_EQ(metrics.data_size(), 4);
-                    auto data = metrics.data(0);
-                    EXPECT_EQ(data.dimensions_in_what().field(),
-                              android::util::SCHEDULED_JOB_STATE_CHANGED);
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                              2);  // job name field
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().
-                                    dimensions_value(0).value_str(),
-                              "job0");  // job name
-                    ValidateAttributionUidAndTagDimension(
-                            data.dimensions_in_condition(),
-                            android::util::SYNC_STATE_CHANGED, 111, "App1");
-                    EXPECT_EQ(data.bucket_info_size(), 1);
-                    EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40 - 11);
-                    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                              bucketStartTimeNs);
-                    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                        bucketStartTimeNs + bucketSizeNs);
-
-                    data = metrics.data(1);
-                    EXPECT_EQ(data.dimensions_in_what().field(),
-                              android::util::SCHEDULED_JOB_STATE_CHANGED);
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                              2);  // job name field
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().
-                                    dimensions_value(0).value_str(),
-                              "job1");  // job name
-                    ValidateAttributionUidAndTagDimension(
-                            data.dimensions_in_condition(),
-                            android::util::SYNC_STATE_CHANGED, 333, "App2");
-                    EXPECT_EQ(data.bucket_info_size(), 1);
-                    EXPECT_EQ(data.bucket_info(0).duration_nanos(), 10);
-                    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + bucketSizeNs);
-                    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                        bucketStartTimeNs + 2 * bucketSizeNs);
-
-                    data = metrics.data(2);
-                    EXPECT_EQ(data.dimensions_in_what().field(),
-                              android::util::SCHEDULED_JOB_STATE_CHANGED);
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                              2);  // job name field
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
-                              "job2");  // job name
-                    ValidateAttributionUidAndTagDimension(
-                            data.dimensions_in_condition(),
-                            android::util::SYNC_STATE_CHANGED, 111, "App1");
-                    EXPECT_EQ(data.bucket_info_size(), 2);
-                    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + bucketSizeNs);
-                    EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201);
-                    EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 650 + 100);
-                    EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + bucketSizeNs);
-                    EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + 2 * bucketSizeNs);
-
-                    data = metrics.data(3);
-                    EXPECT_EQ(data.dimensions_in_what().field(),
-                              android::util::SCHEDULED_JOB_STATE_CHANGED);
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                              2);  // job name field
-                    EXPECT_EQ(data.dimensions_in_what().value_tuple().
-                                    dimensions_value(0).value_str(),
-                              "job2");  // job name
-                    ValidateAttributionUidAndTagDimension(
-                            data.dimensions_in_condition(),
-                            android::util::SYNC_STATE_CHANGED, 333, "App2");
-                    EXPECT_EQ(data.bucket_info_size(), 2);
-                    EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401);
-                    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + bucketSizeNs);
-                    EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 650 + 110);
-                    EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + bucketSizeNs);
-                    EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                              bucketStartTimeNs + 2 * bucketSizeNs);
-                }
-            }
-        }
-    }
-}
-
-namespace {
-
-StatsdConfig CreateDurationMetricConfig_Link_AND_CombinationCondition(
-        DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
-    StatsdConfig config;
-    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-    *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
-    *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
-    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-
-    auto scheduledJobPredicate = CreateScheduledJobPredicate();
-    auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *dimensions = CreateAttributionUidDimensions(
-                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-    dimensions->add_child()->set_field(2);  // job name field.
-
-    auto isSyncingPredicate = CreateIsSyncingPredicate();
-    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidDimensions(
-            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-    if (addExtraDimensionInCondition) {
-        syncDimension->add_child()->set_field(2 /* name field*/);
-    }
-
-    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
-
-    *config.add_predicate() = scheduledJobPredicate;
-    *config.add_predicate() = screenIsOffPredicate;
-    *config.add_predicate() = isSyncingPredicate;
-    auto combinationPredicate = config.add_predicate();
-    combinationPredicate->set_id(StringToId("CombinationPredicate"));
-    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
-    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
-    addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
-    auto metric = config.add_duration_metric();
-    metric->set_bucket(FIVE_MINUTES);
-    metric->set_id(StringToId("scheduledJob"));
-    metric->set_what(scheduledJobPredicate.id());
-    metric->set_condition(combinationPredicate->id());
-    metric->set_aggregation_type(aggregationType);
-    *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
-            android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-
-    auto links = metric->add_links();
-    links->set_condition(isSyncingPredicate.id());
-    *links->mutable_fields_in_what() =
-            CreateAttributionUidDimensions(
-                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-    *links->mutable_fields_in_condition() =
-            CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-    return config;
-}
-
-}  // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition) {
-    for (bool isFullLink : {true, false}) {
-        for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
-            ConfigKey cfgKey;
-            auto config = CreateDurationMetricConfig_Link_AND_CombinationCondition(
-                aggregationType, !isFullLink);
-            int64_t bucketStartTimeNs = 10000000000;
-            int64_t bucketSizeNs =
-                    TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
-            auto processor = CreateStatsLogProcessor(
-                    bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-            EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-            EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
-            std::vector<AttributionNodeInternal> attributions1 = {
-                    CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-                    CreateAttribution(222, "GMSCoreModule2")};
-
-            std::vector<AttributionNodeInternal> attributions2 = {
-                    CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-                    CreateAttribution(555, "GMSCoreModule2")};
-
-            std::vector<AttributionNodeInternal> attributions3 = {
-                    CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
-                    CreateAttribution(555, "GMSCoreModule2")};
-
-            std::vector<std::unique_ptr<LogEvent>> events;
-
-            events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                           bucketStartTimeNs + 55));
-            events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                           bucketStartTimeNs + 120));
-            events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                           bucketStartTimeNs + 121));
-            events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                           bucketStartTimeNs + 450));
-
-            events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                           bucketStartTimeNs + 501));
-            events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                           bucketStartTimeNs + bucketSizeNs + 100));
-
-            events.push_back(CreateStartScheduledJobEvent(
-                    {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
-            events.push_back(CreateFinishScheduledJobEvent(
-                    {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
-
-            events.push_back(CreateStartScheduledJobEvent(
-                    {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
-            events.push_back(CreateFinishScheduledJobEvent(
-                    {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
-            events.push_back(CreateStartScheduledJobEvent(
-                    {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
-            events.push_back(CreateFinishScheduledJobEvent(
-                    {CreateAttribution(333, "App2")}, "job2",
-                    bucketStartTimeNs + bucketSizeNs + 850));
-
-            events.push_back(
-                CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
-                                             bucketStartTimeNs + bucketSizeNs - 2));
-            events.push_back(
-                CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
-                                              bucketStartTimeNs + bucketSizeNs + 900));
-
-            events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                                  bucketStartTimeNs + 50));
-            events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                                bucketStartTimeNs + 110));
-
-            events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
-                                                  bucketStartTimeNs + 300));
-            events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-                                                bucketStartTimeNs + bucketSizeNs + 700));
-            events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
-                                                  bucketStartTimeNs + 400));
-            events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
-                                                bucketStartTimeNs + bucketSizeNs - 1));
-
-            events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
-                                                  bucketStartTimeNs + 550));
-            events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
-                                                bucketStartTimeNs + 800));
-            events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
-                                                  bucketStartTimeNs + bucketSizeNs - 1));
-            events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
-                                                bucketStartTimeNs + bucketSizeNs + 700));
-
-            sortLogEventsByTimestamp(&events);
-
-            for (const auto& event : events) {
-                processor->OnLogEvent(event.get());
-            }
-
-            ConfigMetricsReportList reports;
-            vector<uint8_t> buffer;
-            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                    true, ADB_DUMP, FAST, &buffer);
-            EXPECT_TRUE(buffer.size() > 0);
-            EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-            backfillDimensionPath(&reports);
-            backfillStringInReport(&reports);
-            backfillStartEndTimestamp(&reports);
-
-            EXPECT_EQ(reports.reports_size(), 1);
-            EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-            StatsLogReport::DurationMetricDataWrapper metrics;
-            sortMetricDataByDimensionsValue(
-                    reports.reports(0).metrics(0).duration_metrics(), &metrics);
-
-            if (aggregationType == DurationMetric::SUM) {
-                EXPECT_EQ(metrics.data_size(), 3);
-                auto data = metrics.data(0);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                    bucketStartTimeNs + bucketSizeNs);
-
-                data = metrics.data(1);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 300 + bucketSizeNs - 600);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-
-                data = metrics.data(2);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-            } else {
-                EXPECT_EQ(metrics.data_size(), 3);
-                auto data = metrics.data(0);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                    bucketStartTimeNs + bucketSizeNs);
-
-                data = metrics.data(1);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 300);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-
-                data = metrics.data(2);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-            }
-        }
-    }
-}
-
-namespace {
-
-StatsdConfig CreateDurationMetricConfig_PartialLink_AND_CombinationCondition(
-        DurationMetric::AggregationType aggregationType, bool hashStringInReport) {
-    StatsdConfig config;
-    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-    *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
-    *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
-    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-
-    auto scheduledJobPredicate = CreateScheduledJobPredicate();
-    auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *dimensions = CreateAttributionUidDimensions(
-                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-    dimensions->add_child()->set_field(2);  // job name field.
-
-    auto isSyncingPredicate = CreateIsSyncingPredicate();
-    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidDimensions(
-            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-    syncDimension->add_child()->set_field(2 /* name field*/);
-
-    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
-
-    config.set_hash_strings_in_metric_report(hashStringInReport);
-    *config.add_predicate() = scheduledJobPredicate;
-    *config.add_predicate() = screenIsOffPredicate;
-    *config.add_predicate() = isSyncingPredicate;
-    auto combinationPredicate = config.add_predicate();
-    combinationPredicate->set_id(StringToId("CombinationPredicate"));
-    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
-    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
-    addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
-    auto metric = config.add_duration_metric();
-    metric->set_bucket(FIVE_MINUTES);
-    metric->set_id(StringToId("scheduledJob"));
-    metric->set_what(scheduledJobPredicate.id());
-    metric->set_condition(combinationPredicate->id());
-    metric->set_aggregation_type(aggregationType);
-    *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
-            android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-    *metric->mutable_dimensions_in_condition() = *syncDimension;
-
-
-    auto links = metric->add_links();
-    links->set_condition(isSyncingPredicate.id());
-    *links->mutable_fields_in_what() =
-            CreateAttributionUidDimensions(
-                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-    *links->mutable_fields_in_condition() =
-            CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-    return config;
-}
-
-}  // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition) {
-    for (const bool hashStringInReport : {true, false}) {
-        for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
-            ConfigKey cfgKey;
-            auto config =
-                    CreateDurationMetricConfig_PartialLink_AND_CombinationCondition(
-                            aggregationType, hashStringInReport);
-            int64_t bucketStartTimeNs = 10000000000;
-            int64_t bucketSizeNs =
-                    TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
-            auto processor = CreateStatsLogProcessor(
-                    bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-            EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-            EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
-            std::vector<AttributionNodeInternal> attributions1 = {
-                    CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-                    CreateAttribution(222, "GMSCoreModule2")};
-
-            std::vector<AttributionNodeInternal> attributions2 = {
-                    CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-                    CreateAttribution(555, "GMSCoreModule2")};
-
-            std::vector<AttributionNodeInternal> attributions3 = {
-                    CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
-                    CreateAttribution(555, "GMSCoreModule2")};
-
-            std::vector<std::unique_ptr<LogEvent>> events;
-
-            events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                           bucketStartTimeNs + 55));
-            events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                           bucketStartTimeNs + 120));
-            events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                           bucketStartTimeNs + 121));
-            events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                           bucketStartTimeNs + 450));
-
-            events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                           bucketStartTimeNs + 501));
-            events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                           bucketStartTimeNs + bucketSizeNs + 100));
-
-            events.push_back(CreateStartScheduledJobEvent(
-                    {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
-            events.push_back(CreateFinishScheduledJobEvent(
-                    {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
-
-            events.push_back(CreateStartScheduledJobEvent(
-                    {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
-            events.push_back(CreateFinishScheduledJobEvent(
-                    {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
-            events.push_back(CreateStartScheduledJobEvent(
-                    {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
-            events.push_back(CreateFinishScheduledJobEvent(
-                    {CreateAttribution(333, "App2")}, "job2",
-                    bucketStartTimeNs + bucketSizeNs + 850));
-
-            events.push_back(
-                CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
-                                             bucketStartTimeNs + bucketSizeNs - 2));
-            events.push_back(
-                CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
-                                              bucketStartTimeNs + bucketSizeNs + 900));
-
-            events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                                  bucketStartTimeNs + 50));
-            events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                                bucketStartTimeNs + 110));
-
-            events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
-                                                  bucketStartTimeNs + 300));
-            events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-                                                bucketStartTimeNs + bucketSizeNs + 700));
-            events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
-                                                  bucketStartTimeNs + 400));
-            events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
-                                                bucketStartTimeNs + bucketSizeNs - 1));
-
-            events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
-                                                  bucketStartTimeNs + 550));
-            events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
-                                                bucketStartTimeNs + 800));
-            events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
-                                                  bucketStartTimeNs + bucketSizeNs - 1));
-            events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
-                                                bucketStartTimeNs + bucketSizeNs + 700));
-
-            sortLogEventsByTimestamp(&events);
-
-            for (const auto& event : events) {
-                processor->OnLogEvent(event.get());
-            }
-
-            ConfigMetricsReportList reports;
-            vector<uint8_t> buffer;
-            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                    true, ADB_DUMP, FAST, &buffer);
-            EXPECT_TRUE(buffer.size() > 0);
-            EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-            backfillDimensionPath(&reports);
-            backfillStringInReport(&reports);
-            backfillStartEndTimestamp(&reports);
-
-            EXPECT_EQ(reports.reports_size(), 1);
-            EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-            StatsLogReport::DurationMetricDataWrapper metrics;
-            sortMetricDataByDimensionsValue(
-                    reports.reports(0).metrics(0).duration_metrics(), &metrics);
-            if (aggregationType == DurationMetric::SUM) {
-                EXPECT_EQ(metrics.data_size(), 4);
-                auto data = metrics.data(0);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
-                EXPECT_EQ("ReadEmail",
-                          data.dimensions_in_condition().value_tuple().
-                                dimensions_value(1).value_str());
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-
-                data = metrics.data(1);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
-                EXPECT_EQ("ReadDoc",
-                          data.dimensions_in_condition().value_tuple().
-                                dimensions_value(1).value_str());
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 1 - 600 + 50);
-
-                data = metrics.data(2);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
-                EXPECT_EQ("ReadEmail",
-                          data.dimensions_in_condition().value_tuple().
-                                dimensions_value(1).value_str());
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 300 + bucketSizeNs - 600);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-
-                data = metrics.data(3);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
-                EXPECT_EQ("ReadDoc",
-                          data.dimensions_in_condition().value_tuple().
-                                dimensions_value(1).value_str());
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-            } else {
-                EXPECT_EQ(metrics.data_size(), 4);
-                auto data = metrics.data(0);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
-                EXPECT_EQ("ReadEmail",
-                          data.dimensions_in_condition().value_tuple().
-                                dimensions_value(1).value_str());
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                    bucketStartTimeNs + bucketSizeNs);
-
-                data = metrics.data(1);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
-                EXPECT_EQ("ReadDoc",
-                          data.dimensions_in_condition().value_tuple().
-                                dimensions_value(1).value_str());
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 50);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 1 - 600);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-
-                data = metrics.data(2);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
-                EXPECT_EQ("ReadEmail",
-                          data.dimensions_in_condition().value_tuple().
-                                dimensions_value(1).value_str());
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 300);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-
-                data = metrics.data(3);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
-                EXPECT_EQ("ReadDoc",
-                          data.dimensions_in_condition().value_tuple().
-                                dimensions_value(1).value_str());
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-            }
-        }
-    }
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
deleted file mode 100644
index f3ecd56..0000000
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
+++ /dev/null
@@ -1,816 +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.
-
-#include <gtest/gtest.h>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-StatsdConfig CreateCountMetric_NoLink_CombinationCondition_Config() {
-    StatsdConfig config;
-    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-    auto screenBrightnessChangeAtomMatcher = CreateScreenBrightnessChangedAtomMatcher();
-    *config.add_atom_matcher() = screenBrightnessChangeAtomMatcher;
-    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
-    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
-    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
-
-    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
-    *config.add_predicate() = screenIsOffPredicate;
-
-    auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
-    // The predicate is dimensioning by any attribution node and both by uid and tag.
-    *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
-            CreateAttributionUidAndTagDimensions(android::util::WAKELOCK_STATE_CHANGED,
-                                                 {Position::FIRST});
-    *config.add_predicate() = holdingWakelockPredicate;
-
-    auto combinationPredicate = config.add_predicate();
-    combinationPredicate->set_id(987654);
-    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
-    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
-    addPredicateToPredicateCombination(holdingWakelockPredicate, combinationPredicate);
-
-    auto metric = config.add_count_metric();
-    metric->set_id(StringToId("ScreenBrightnessChangeMetric"));
-    metric->set_what(screenBrightnessChangeAtomMatcher.id());
-    metric->set_condition(combinationPredicate->id());
-    *metric->mutable_dimensions_in_what() =
-            CreateDimensions(android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */});
-    *metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions(
-            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
-    metric->set_bucket(FIVE_MINUTES);
-    return config;
-}
-
-}  // namespace
-
-TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition) {
-    ConfigKey cfgKey;
-    auto config = CreateCountMetric_NoLink_CombinationCondition_Config();
-    int64_t bucketStartTimeNs = 10000000000;
-    int64_t bucketSizeNs =
-            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
-
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
-    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
-                                                          CreateAttribution(222, "GMSCoreModule1"),
-                                                          CreateAttribution(222, "GMSCoreModule2")};
-
-    std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(333, "App2"),
-                                                          CreateAttribution(222, "GMSCoreModule1"),
-                                                          CreateAttribution(555, "GMSCoreModule2")};
-
-    std::vector<std::unique_ptr<LogEvent>> events;
-    events.push_back(
-            CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 100));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + bucketSizeNs + 1));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 2 * bucketSizeNs - 10));
-
-    events.push_back(CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 200));
-    events.push_back(
-            CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1));
-
-    events.push_back(CreateAcquireWakelockEvent(attributions2, "wl2",
-                                                bucketStartTimeNs + bucketSizeNs - 100));
-    events.push_back(CreateReleaseWakelockEvent(attributions2, "wl2",
-                                                bucketStartTimeNs + 2 * bucketSizeNs - 50));
-
-    events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 11));
-    events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 101));
-    events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 201));
-    events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + 203));
-    events.push_back(
-            CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs - 99));
-    events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs - 2));
-    events.push_back(CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + bucketSizeNs - 1));
-    events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs + 2));
-    events.push_back(
-            CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 11));
-    events.push_back(
-            CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 9));
-    events.push_back(
-            CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 1));
-
-    sortLogEventsByTimestamp(&events);
-
-    for (const auto& event : events) {
-        processor->OnLogEvent(event.get());
-    }
-
-    ConfigMetricsReportList reports;
-    vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
-                            ADB_DUMP, FAST, &buffer);
-    EXPECT_TRUE(buffer.size() > 0);
-    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-    backfillDimensionPath(&reports);
-    backfillStringInReport(&reports);
-    backfillStartEndTimestamp(&reports);
-
-    EXPECT_EQ(reports.reports_size(), 1);
-    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-    StatsLogReport::CountMetricDataWrapper countMetrics;
-    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
-
-    EXPECT_EQ(countMetrics.data_size(), 7);
-    auto data = countMetrics.data(0);
-    EXPECT_EQ(data.bucket_info_size(), 1);
-    EXPECT_EQ(data.bucket_info(0).count(), 1);
-    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
-    EXPECT_FALSE(data.dimensions_in_condition().has_field());
-
-    data = countMetrics.data(1);
-    EXPECT_EQ(data.bucket_info_size(), 1);
-    EXPECT_EQ(data.bucket_info(0).count(), 1);
-    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
-    ValidateAttributionUidDimension(data.dimensions_in_condition(),
-                                    android::util::WAKELOCK_STATE_CHANGED, 111);
-
-    data = countMetrics.data(2);
-    EXPECT_EQ(data.bucket_info_size(), 1);
-    EXPECT_EQ(data.bucket_info(0).count(), 3);
-    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
-    ValidateAttributionUidDimension(data.dimensions_in_condition(),
-                                    android::util::WAKELOCK_STATE_CHANGED, 111);
-
-    data = countMetrics.data(3);
-    EXPECT_EQ(data.bucket_info_size(), 2);
-    EXPECT_EQ(data.bucket_info(0).count(), 2);
-    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-    EXPECT_EQ(data.bucket_info(1).count(), 1);
-    EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-    EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
-    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
-    ValidateAttributionUidDimension(data.dimensions_in_condition(),
-                                    android::util::WAKELOCK_STATE_CHANGED, 333);
-
-    data = countMetrics.data(4);
-    EXPECT_EQ(data.bucket_info_size(), 1);
-    EXPECT_EQ(data.bucket_info(0).count(), 2);
-    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
-    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
-    EXPECT_FALSE(data.dimensions_in_condition().has_field());
-
-    data = countMetrics.data(5);
-    EXPECT_EQ(data.bucket_info_size(), 1);
-    EXPECT_EQ(data.bucket_info(0).count(), 1);
-    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
-    ValidateAttributionUidDimension(data.dimensions_in_condition(),
-                                    android::util::WAKELOCK_STATE_CHANGED, 111);
-
-    data = countMetrics.data(6);
-    EXPECT_EQ(data.bucket_info_size(), 1);
-    EXPECT_EQ(data.bucket_info(0).count(), 1);
-    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
-    ValidateAttributionUidDimension(data.dimensions_in_condition(),
-                                    android::util::WAKELOCK_STATE_CHANGED, 333);
-}
-
-namespace {
-
-StatsdConfig CreateCountMetric_Link_CombinationCondition() {
-    StatsdConfig config;
-    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-    auto appCrashMatcher = CreateProcessCrashAtomMatcher();
-    *config.add_atom_matcher() = appCrashMatcher;
-    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
-    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-
-    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
-    auto isSyncingPredicate = CreateIsSyncingPredicate();
-    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
-                                                          {Position::FIRST});
-    syncDimension->add_child()->set_field(2 /* name field*/);
-
-    *config.add_predicate() = screenIsOffPredicate;
-    *config.add_predicate() = isSyncingPredicate;
-    auto combinationPredicate = config.add_predicate();
-    combinationPredicate->set_id(987654);
-    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
-    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
-    addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
-    auto metric = config.add_count_metric();
-    metric->set_bucket(FIVE_MINUTES);
-    metric->set_id(StringToId("AppCrashMetric"));
-    metric->set_what(appCrashMatcher.id());
-    metric->set_condition(combinationPredicate->id());
-    *metric->mutable_dimensions_in_what() =
-            CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */});
-    *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
-            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-
-    // Links between crash atom and condition of app is in syncing.
-    auto links = metric->add_links();
-    links->set_condition(isSyncingPredicate.id());
-    auto dimensionWhat = links->mutable_fields_in_what();
-    dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
-    dimensionWhat->add_child()->set_field(1);  // uid field.
-    *links->mutable_fields_in_condition() =
-            CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-    return config;
-}
-
-}  // namespace
-
-TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition) {
-    ConfigKey cfgKey;
-    auto config = CreateCountMetric_Link_CombinationCondition();
-    int64_t bucketStartTimeNs = 10000000000;
-    int64_t bucketSizeNs =
-            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
-
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
-                                                          CreateAttribution(222, "GMSCoreModule1"),
-                                                          CreateAttribution(222, "GMSCoreModule2")};
-
-    std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(333, "App2"),
-                                                          CreateAttribution(222, "GMSCoreModule1"),
-                                                          CreateAttribution(555, "GMSCoreModule2")};
-
-    std::vector<std::unique_ptr<LogEvent>> events;
-
-    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 11));
-    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 101));
-    events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 101));
-
-    events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 201));
-    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 211));
-    events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 211));
-
-    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 401));
-    events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 401));
-    events.push_back(CreateAppCrashEvent(555, bucketStartTimeNs + 401));
-
-    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + bucketSizeNs + 301));
-    events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + bucketSizeNs + 301));
-
-    events.push_back(CreateAppCrashEvent(777, bucketStartTimeNs + bucketSizeNs + 701));
-
-    events.push_back(
-            CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 100));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + 202));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + bucketSizeNs + 700));
-
-    events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
-    events.push_back(
-            CreateSyncEndEvent(attributions1, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300));
-
-    events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
-    events.push_back(
-            CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1));
-
-    events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 400));
-    events.push_back(
-            CreateSyncEndEvent(attributions2, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 600));
-
-    sortLogEventsByTimestamp(&events);
-
-    for (const auto& event : events) {
-        processor->OnLogEvent(event.get());
-    }
-
-    ConfigMetricsReportList reports;
-    vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
-                            ADB_DUMP, FAST, &buffer);
-    EXPECT_TRUE(buffer.size() > 0);
-    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-    backfillDimensionPath(&reports);
-    backfillStringInReport(&reports);
-    backfillStartEndTimestamp(&reports);
-
-    EXPECT_EQ(reports.reports_size(), 1);
-    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-    StatsLogReport::CountMetricDataWrapper countMetrics;
-    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
-
-    EXPECT_EQ(countMetrics.data_size(), 5);
-    auto data = countMetrics.data(0);
-    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
-    EXPECT_FALSE(data.dimensions_in_condition().has_field());
-    EXPECT_EQ(data.bucket_info_size(), 1);
-    EXPECT_EQ(data.bucket_info(0).count(), 1);
-    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-
-    data = countMetrics.data(1);
-    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
-    ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                          android::util::SYNC_STATE_CHANGED, 111, "App1");
-    EXPECT_EQ(data.bucket_info_size(), 1);
-    EXPECT_EQ(data.bucket_info(0).count(), 2);
-    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-
-    data = countMetrics.data(2);
-    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 222);
-    EXPECT_FALSE(data.dimensions_in_condition().has_field());
-    EXPECT_EQ(data.bucket_info_size(), 1);
-    EXPECT_EQ(data.bucket_info(0).count(), 2);
-    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-
-    data = countMetrics.data(3);
-    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
-    ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                          android::util::SYNC_STATE_CHANGED, 333, "App2");
-    EXPECT_EQ(data.bucket_info_size(), 2);
-    EXPECT_EQ(data.bucket_info(0).count(), 1);
-    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-    EXPECT_EQ(data.bucket_info(1).count(), 1);
-    EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-    EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
-
-    data = countMetrics.data(4);
-    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 777);
-    EXPECT_FALSE(data.dimensions_in_condition().has_field());
-    EXPECT_EQ(data.bucket_info_size(), 1);
-    EXPECT_EQ(data.bucket_info(0).count(), 1);
-    EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-    EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
-}
-
-namespace {
-
-StatsdConfig CreateDurationMetricConfig_NoLink_CombinationCondition(
-        DurationMetric::AggregationType aggregationType) {
-    StatsdConfig config;
-    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-    *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
-    *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
-    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
-    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-
-    auto inBatterySaverModePredicate = CreateBatterySaverModePredicate();
-
-    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
-    auto isSyncingPredicate = CreateIsSyncingPredicate();
-    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
-                                                          {Position::FIRST});
-    syncDimension->add_child()->set_field(2 /* name field */);
-
-    *config.add_predicate() = inBatterySaverModePredicate;
-    *config.add_predicate() = screenIsOffPredicate;
-    *config.add_predicate() = isSyncingPredicate;
-    auto combinationPredicate = config.add_predicate();
-    combinationPredicate->set_id(987654);
-    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
-    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
-    addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
-    auto metric = config.add_duration_metric();
-    metric->set_bucket(FIVE_MINUTES);
-    metric->set_id(StringToId("BatterySaverModeDurationMetric"));
-    metric->set_what(inBatterySaverModePredicate.id());
-    metric->set_condition(combinationPredicate->id());
-    metric->set_aggregation_type(aggregationType);
-    *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
-            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-    return config;
-}
-
-}  // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition) {
-    for (auto aggregationType : { DurationMetric::MAX_SPARSE, DurationMetric::SUM}) {
-        ConfigKey cfgKey;
-        auto config = CreateDurationMetricConfig_NoLink_CombinationCondition(aggregationType);
-        int64_t bucketStartTimeNs = 10000000000;
-        int64_t bucketSizeNs =
-                TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
-        auto processor = CreateStatsLogProcessor(
-                bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-        EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-        EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
-        std::vector<AttributionNodeInternal> attributions1 = {
-                CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-                CreateAttribution(222, "GMSCoreModule2")};
-
-        std::vector<AttributionNodeInternal> attributions2 = {
-                CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-                CreateAttribution(555, "GMSCoreModule2")};
-
-        std::vector<std::unique_ptr<LogEvent>> events;
-
-        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 1));
-        events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 101));
-        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 110));
-
-        events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 201));
-        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 500));
-
-        events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 600));
-        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 850));
-
-        events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + bucketSizeNs + 870));
-        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 900));
-
-        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                       bucketStartTimeNs + 10));
-        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                       bucketStartTimeNs + 100));
-        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                       bucketStartTimeNs + 202));
-        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                       bucketStartTimeNs + bucketSizeNs + 800));
-
-        events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
-        events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                            bucketStartTimeNs + bucketSizeNs + 300));
-
-        events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
-        events.push_back(
-                CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1));
-
-        events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
-        events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-                                            bucketStartTimeNs + bucketSizeNs + 700));
-
-        sortLogEventsByTimestamp(&events);
-
-        for (const auto& event : events) {
-            processor->OnLogEvent(event.get());
-        }
-
-        ConfigMetricsReportList reports;
-        vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
-                                ADB_DUMP, FAST, &buffer);
-        EXPECT_TRUE(buffer.size() > 0);
-        EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-        backfillDimensionPath(&reports);
-        backfillStringInReport(&reports);
-        backfillStartEndTimestamp(&reports);
-
-        EXPECT_EQ(reports.reports_size(), 1);
-        EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-        StatsLogReport::DurationMetricDataWrapper metrics;
-        sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics);
-
-        EXPECT_EQ(metrics.data_size(), 3);
-        auto data = metrics.data(0);
-        EXPECT_FALSE(data.dimensions_in_what().has_field());
-        EXPECT_FALSE(data.dimensions_in_condition().has_field());
-        if (aggregationType == DurationMetric::SUM) {
-            EXPECT_EQ(data.bucket_info_size(), 2);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30);
-            EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + 2 * bucketSizeNs);
-        } else {
-            EXPECT_EQ(data.bucket_info_size(), 2);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30);
-            EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + 2 * bucketSizeNs);
-        }
-
-        data = metrics.data(1);
-        EXPECT_FALSE(data.dimensions_in_what().has_field());
-        ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                              android::util::SYNC_STATE_CHANGED, 111, "App1");
-        EXPECT_EQ(data.bucket_info_size(), 2);
-
-        if (aggregationType == DurationMetric::SUM) {
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
-            EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
-        } else {
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201);
-            EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 300);
-        }
-        EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-        EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                  bucketStartTimeNs + bucketSizeNs);
-        EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                  bucketStartTimeNs + bucketSizeNs);
-        EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                  bucketStartTimeNs + 2 * bucketSizeNs);
-
-        data = metrics.data(2);
-        EXPECT_FALSE(data.dimensions_in_what().has_field());
-        ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                              android::util::SYNC_STATE_CHANGED, 333, "App2");
-        EXPECT_EQ(data.bucket_info_size(), 2);
-        if (aggregationType == DurationMetric::SUM) {
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
-            EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
-        } else {
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401);
-            EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs + 700 - 600);
-        }
-        EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-        EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                  bucketStartTimeNs + bucketSizeNs);
-        EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                  bucketStartTimeNs + bucketSizeNs);
-        EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                  bucketStartTimeNs + 2 * bucketSizeNs);
-    }
-}
-
-namespace {
-
-StatsdConfig CreateDurationMetricConfig_Link_CombinationCondition(
-        DurationMetric::AggregationType aggregationType) {
-    StatsdConfig config;
-    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-    *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
-    *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
-    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
-    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-
-    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
-    auto isSyncingPredicate = CreateIsSyncingPredicate();
-    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
-                                                          {Position::FIRST});
-    syncDimension->add_child()->set_field(2 /* name field */);
-
-    auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
-    *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
-            CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */});
-
-    *config.add_predicate() = screenIsOffPredicate;
-    *config.add_predicate() = isSyncingPredicate;
-    *config.add_predicate() = isInBackgroundPredicate;
-    auto combinationPredicate = config.add_predicate();
-    combinationPredicate->set_id(987654);
-    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
-    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
-    addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
-    auto metric = config.add_duration_metric();
-    metric->set_bucket(FIVE_MINUTES);
-    metric->set_id(StringToId("AppInBackgroundMetric"));
-    metric->set_what(isInBackgroundPredicate.id());
-    metric->set_condition(combinationPredicate->id());
-    metric->set_aggregation_type(aggregationType);
-    *metric->mutable_dimensions_in_what() =
-            CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */});
-    *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
-            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-
-    // Links between crash atom and condition of app is in syncing.
-    auto links = metric->add_links();
-    links->set_condition(isSyncingPredicate.id());
-    auto dimensionWhat = links->mutable_fields_in_what();
-    dimensionWhat->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
-    dimensionWhat->add_child()->set_field(1);  // uid field.
-    *links->mutable_fields_in_condition() =
-            CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-    return config;
-}
-
-}  // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition) {
-    for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
-        ConfigKey cfgKey;
-        auto config = CreateDurationMetricConfig_Link_CombinationCondition(aggregationType);
-        int64_t bucketStartTimeNs = 10000000000;
-        int64_t bucketSizeNs =
-                TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
-        auto processor = CreateStatsLogProcessor(
-                bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-        EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-        EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
-        std::vector<AttributionNodeInternal> attributions1 = {
-                CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-                CreateAttribution(222, "GMSCoreModule2")};
-
-        std::vector<AttributionNodeInternal> attributions2 = {
-                CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-                CreateAttribution(555, "GMSCoreModule2")};
-
-        std::vector<std::unique_ptr<LogEvent>> events;
-
-        events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 101));
-        events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + 110));
-
-        events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 201));
-        events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + bucketSizeNs + 100));
-
-        events.push_back(CreateMoveToBackgroundEvent(333, bucketStartTimeNs + 399));
-        events.push_back(CreateMoveToForegroundEvent(333, bucketStartTimeNs + bucketSizeNs + 800));
-
-        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                       bucketStartTimeNs + 10));
-        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                       bucketStartTimeNs + 100));
-        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                       bucketStartTimeNs + 202));
-        events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                       bucketStartTimeNs + bucketSizeNs + 801));
-
-        events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
-        events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                            bucketStartTimeNs + bucketSizeNs + 300));
-
-        events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
-        events.push_back(
-                CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1));
-
-        events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
-        events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-                                            bucketStartTimeNs + bucketSizeNs + 700));
-
-        sortLogEventsByTimestamp(&events);
-
-        for (const auto& event : events) {
-            processor->OnLogEvent(event.get());
-        }
-
-        ConfigMetricsReportList reports;
-        vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
-                                ADB_DUMP, FAST, &buffer);
-        EXPECT_TRUE(buffer.size() > 0);
-        EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-        backfillDimensionPath(&reports);
-        backfillStringInReport(&reports);
-        backfillStartEndTimestamp(&reports);
-
-        EXPECT_EQ(reports.reports_size(), 1);
-        EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-        StatsLogReport::DurationMetricDataWrapper metrics;
-        sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics);
-
-        EXPECT_EQ(metrics.data_size(), 3);
-        auto data = metrics.data(0);
-        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
-        EXPECT_FALSE(data.dimensions_in_condition().has_field());
-        EXPECT_EQ(data.bucket_info_size(), 1);
-        EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
-        EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-        EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-
-        data = metrics.data(1);
-        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
-        ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                              android::util::SYNC_STATE_CHANGED, 111, "App1");
-        if (aggregationType == DurationMetric::SUM) {
-            EXPECT_EQ(data.bucket_info_size(), 2);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201);
-            EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + 2 * bucketSizeNs);
-        } else {
-            EXPECT_EQ(data.bucket_info_size(), 1);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs + 100 - 201);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + 2 * bucketSizeNs);
-        }
-
-        data = metrics.data(2);
-        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
-        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
-        ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                              android::util::SYNC_STATE_CHANGED, 333, "App2");
-        if (aggregationType == DurationMetric::SUM) {
-            EXPECT_EQ(data.bucket_info_size(), 2);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401);
-            EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + 2 * bucketSizeNs);
-        } else {
-            EXPECT_EQ(data.bucket_info_size(), 1);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs + 299);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + 2 * bucketSizeNs);
-        }
-    }
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
deleted file mode 100644
index 489bb0b..0000000
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
+++ /dev/null
@@ -1,814 +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.
-
-#include <gtest/gtest.h>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-StatsdConfig CreateDurationMetricConfig_NoLink_SimpleCondition(
-        DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
-    StatsdConfig config;
-    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-    *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
-    *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-
-    auto scheduledJobPredicate = CreateScheduledJobPredicate();
-    auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
-    dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
-    dimensions->add_child()->set_field(2);  // job name field.
-
-    auto isSyncingPredicate = CreateIsSyncingPredicate();
-    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
-                                                          {Position::FIRST});
-    if (addExtraDimensionInCondition) {
-        syncDimension->add_child()->set_field(2 /* name field*/);
-    }
-
-    *config.add_predicate() = scheduledJobPredicate;
-    *config.add_predicate() = isSyncingPredicate;
-
-    auto metric = config.add_duration_metric();
-    metric->set_bucket(FIVE_MINUTES);
-    metric->set_id(StringToId("scheduledJob"));
-    metric->set_what(scheduledJobPredicate.id());
-    metric->set_condition(isSyncingPredicate.id());
-    metric->set_aggregation_type(aggregationType);
-    auto dimensionWhat = metric->mutable_dimensions_in_what();
-    dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
-    dimensionWhat->add_child()->set_field(2);  // job name field.
-    *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
-            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-    return config;
-}
-
-}  // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition) {
-    for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : {true, false}) {
-        for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
-            ConfigKey cfgKey;
-            auto config = CreateDurationMetricConfig_NoLink_SimpleCondition(
-                    aggregationType, isDimensionInConditionSubSetOfConditionTrackerDimension);
-            int64_t bucketStartTimeNs = 10000000000;
-            int64_t bucketSizeNs =
-                    TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
-            auto processor = CreateStatsLogProcessor(
-                    bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-            EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-            EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
-            std::vector<AttributionNodeInternal> attributions1 = {
-                    CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-                    CreateAttribution(222, "GMSCoreModule2")};
-
-            std::vector<AttributionNodeInternal> attributions2 = {
-                    CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-                    CreateAttribution(555, "GMSCoreModule2")};
-
-            std::vector<std::unique_ptr<LogEvent>> events;
-
-            events.push_back(CreateStartScheduledJobEvent(
-                    {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 1));
-            events.push_back(CreateFinishScheduledJobEvent(
-                    {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101));
-
-            events.push_back(CreateStartScheduledJobEvent(
-                    {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201));
-            events.push_back(CreateFinishScheduledJobEvent(
-                    {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500));
-
-            events.push_back(CreateStartScheduledJobEvent(
-                    {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600));
-            events.push_back(CreateFinishScheduledJobEvent(
-                    {CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850));
-
-            events.push_back(CreateStartScheduledJobEvent(
-                    {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600));
-            events.push_back(CreateFinishScheduledJobEvent(
-                    {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900));
-
-            events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                                  bucketStartTimeNs + 10));
-            events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                                bucketStartTimeNs + 50));
-
-            events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                                  bucketStartTimeNs + 200));
-            events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                                bucketStartTimeNs + bucketSizeNs + 300));
-
-            events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc",
-                                                  bucketStartTimeNs + 400));
-            events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
-                                                bucketStartTimeNs + bucketSizeNs - 1));
-
-            events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
-                                                  bucketStartTimeNs + 401));
-            events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-                                                bucketStartTimeNs + bucketSizeNs + 700));
-
-            sortLogEventsByTimestamp(&events);
-
-            for (const auto& event : events) {
-                processor->OnLogEvent(event.get());
-            }
-
-            ConfigMetricsReportList reports;
-            vector<uint8_t> buffer;
-            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                    true, ADB_DUMP, FAST, &buffer);
-            EXPECT_TRUE(buffer.size() > 0);
-            EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-            backfillDimensionPath(&reports);
-            backfillStringInReport(&reports);
-            backfillStartEndTimestamp(&reports);
-
-            EXPECT_EQ(reports.reports_size(), 1);
-            EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-            StatsLogReport::DurationMetricDataWrapper metrics;
-            sortMetricDataByDimensionsValue(
-                    reports.reports(0).metrics(0).duration_metrics(), &metrics);
-            if (aggregationType == DurationMetric::SUM) {
-                EXPECT_EQ(metrics.data_size(), 4);
-                auto data = metrics.data(0);
-                EXPECT_EQ(data.dimensions_in_what().field(),
-                          android::util::SCHEDULED_JOB_STATE_CHANGED);
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                          2);  // job name field
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
-                          "job0");  // job name
-                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                                      android::util::SYNC_STATE_CHANGED, 111, "App1");
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                    bucketStartTimeNs + bucketSizeNs);
-
-                data = metrics.data(1);
-                EXPECT_EQ(data.dimensions_in_what().field(),
-                          android::util::SCHEDULED_JOB_STATE_CHANGED);
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                          2);  // job name field
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
-                          "job1");  // job name
-                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                                      android::util::SYNC_STATE_CHANGED, 333, "App2");
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                    bucketStartTimeNs + 2 * bucketSizeNs);
-
-                data = metrics.data(2);
-                EXPECT_EQ(data.dimensions_in_what().field(),
-                          android::util::SCHEDULED_JOB_STATE_CHANGED);
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                          2);  // job name field
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
-                          "job2");  // job name
-                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                                      android::util::SYNC_STATE_CHANGED, 111, "App1");
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-
-                data = metrics.data(3);
-                EXPECT_EQ(data.dimensions_in_what().field(),
-                          android::util::SCHEDULED_JOB_STATE_CHANGED);
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                          2);  // job name field
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
-                          "job2");  // job name
-                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                                      android::util::SYNC_STATE_CHANGED, 333, "App2");
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-            } else {
-                EXPECT_EQ(metrics.data_size(), 4);
-                auto data = metrics.data(0);
-                EXPECT_EQ(data.dimensions_in_what().field(),
-                          android::util::SCHEDULED_JOB_STATE_CHANGED);
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                          2);  // job name field
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
-                          "job0");  // job name
-                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                                      android::util::SYNC_STATE_CHANGED, 111, "App1");
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                    bucketStartTimeNs + bucketSizeNs);
-
-                data = metrics.data(1);
-                EXPECT_EQ(data.dimensions_in_what().field(),
-                          android::util::SCHEDULED_JOB_STATE_CHANGED);
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                          2);  // job name field
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
-                          "job1");  // job name
-                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                                      android::util::SYNC_STATE_CHANGED, 333, "App2");
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                    bucketStartTimeNs + 2 * bucketSizeNs);
-
-                data = metrics.data(2);
-                EXPECT_EQ(data.dimensions_in_what().field(),
-                          android::util::SCHEDULED_JOB_STATE_CHANGED);
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                          2);  // job name field
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
-                          "job2");  // job name
-                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                                      android::util::SYNC_STATE_CHANGED, 111, "App1");
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 300);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-
-                data = metrics.data(3);
-                EXPECT_EQ(data.dimensions_in_what().field(),
-                          android::util::SCHEDULED_JOB_STATE_CHANGED);
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
-                          2);  // job name field
-                EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
-                          "job2");  // job name
-                ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
-                                                      android::util::SYNC_STATE_CHANGED, 333, "App2");
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 );
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-            }
-        }
-    }
-}
-
-namespace {
-
-StatsdConfig createDurationMetric_Link_SimpleConditionConfig(
-        DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
-    StatsdConfig config;
-    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-    *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
-    *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-
-    auto scheduledJobPredicate = CreateScheduledJobPredicate();
-    auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *dimensions = CreateAttributionUidDimensions(
-                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-    dimensions->add_child()->set_field(2);  // job name field.
-
-    auto isSyncingPredicate = CreateIsSyncingPredicate();
-    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidDimensions(
-            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-    if (addExtraDimensionInCondition) {
-        syncDimension->add_child()->set_field(2 /* name field*/);
-    }
-
-    *config.add_predicate() = scheduledJobPredicate;
-    *config.add_predicate() = isSyncingPredicate;
-
-    auto metric = config.add_duration_metric();
-    metric->set_bucket(FIVE_MINUTES);
-    metric->set_id(StringToId("scheduledJob"));
-    metric->set_what(scheduledJobPredicate.id());
-    metric->set_condition(isSyncingPredicate.id());
-    metric->set_aggregation_type(aggregationType);
-    *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
-            android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-
-    auto links = metric->add_links();
-    links->set_condition(isSyncingPredicate.id());
-    *links->mutable_fields_in_what() =
-            CreateAttributionUidDimensions(
-                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-    *links->mutable_fields_in_condition() =
-            CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-    return config;
-}
-
-}  // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition) {
-    for (bool isFullLink : {true, false}) {
-        for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
-            ConfigKey cfgKey;
-            auto config = createDurationMetric_Link_SimpleConditionConfig(
-                    aggregationType, !isFullLink);
-            int64_t bucketStartTimeNs = 10000000000;
-            int64_t bucketSizeNs =
-                    TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
-            auto processor = CreateStatsLogProcessor(
-                    bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-            EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-            EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
-            std::vector<AttributionNodeInternal> attributions1 = {
-                    CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-                    CreateAttribution(222, "GMSCoreModule2")};
-
-            std::vector<AttributionNodeInternal> attributions2 = {
-                    CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-                    CreateAttribution(555, "GMSCoreModule2")};
-
-            std::vector<AttributionNodeInternal> attributions3 = {
-                    CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
-                    CreateAttribution(555, "GMSCoreModule2")};
-
-            std::vector<std::unique_ptr<LogEvent>> events;
-
-            events.push_back(CreateStartScheduledJobEvent(
-                    {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
-            events.push_back(CreateFinishScheduledJobEvent(
-                    {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
-
-            events.push_back(CreateStartScheduledJobEvent(
-                    {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
-            events.push_back(CreateFinishScheduledJobEvent(
-                    {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
-            events.push_back(CreateStartScheduledJobEvent(
-                    {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
-            events.push_back(
-                CreateFinishScheduledJobEvent({CreateAttribution(333, "App2")}, "job2",
-                                               bucketStartTimeNs + bucketSizeNs + 850));
-
-            events.push_back(
-                CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
-                                             bucketStartTimeNs + bucketSizeNs - 2));
-            events.push_back(
-                CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
-                                              bucketStartTimeNs + bucketSizeNs + 900));
-
-            events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                                  bucketStartTimeNs + 50));
-            events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                                bucketStartTimeNs + 110));
-
-            events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
-                                                  bucketStartTimeNs + 300));
-            events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-                                                bucketStartTimeNs + bucketSizeNs + 700));
-            events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
-                                                  bucketStartTimeNs + 400));
-            events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
-                                                bucketStartTimeNs + bucketSizeNs - 1));
-
-            events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
-                                                  bucketStartTimeNs + 550));
-            events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
-                                                bucketStartTimeNs + 800));
-            events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
-                                                  bucketStartTimeNs + bucketSizeNs - 1));
-            events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
-                                                bucketStartTimeNs + bucketSizeNs + 700));
-
-            sortLogEventsByTimestamp(&events);
-
-            for (const auto& event : events) {
-                processor->OnLogEvent(event.get());
-            }
-
-            ConfigMetricsReportList reports;
-            vector<uint8_t> buffer;
-            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
-                                    true, ADB_DUMP, FAST, &buffer);
-            EXPECT_TRUE(buffer.size() > 0);
-            EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-            backfillDimensionPath(&reports);
-            backfillStringInReport(&reports);
-            backfillStartEndTimestamp(&reports);
-
-            EXPECT_EQ(reports.reports_size(), 1);
-            EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-            StatsLogReport::DurationMetricDataWrapper metrics;
-            sortMetricDataByDimensionsValue(
-                    reports.reports(0).metrics(0).duration_metrics(), &metrics);
-
-            if (aggregationType == DurationMetric::SUM) {
-                EXPECT_EQ(metrics.data_size(), 3);
-                auto data = metrics.data(0);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                    bucketStartTimeNs + bucketSizeNs);
-
-                data = metrics.data(1);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300 + bucketSizeNs - 600);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-
-                data = metrics.data(2);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-            } else {
-                EXPECT_EQ(metrics.data_size(), 3);
-                auto data = metrics.data(0);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                    bucketStartTimeNs + bucketSizeNs);
-
-                data = metrics.data(1);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
-                EXPECT_EQ(data.bucket_info_size(), 2);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300);
-                EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
-                EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-
-                data = metrics.data(2);
-                ValidateAttributionUidDimension(
-                    data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
-                EXPECT_EQ(data.bucket_info_size(), 1);
-                EXPECT_EQ(data.bucket_info(0).duration_nanos(), 701);
-                EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + bucketSizeNs);
-                EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                          bucketStartTimeNs + 2 * bucketSizeNs);
-            }
-        }
-    }
-}
-
-namespace {
-
-StatsdConfig createDurationMetric_PartialLink_SimpleConditionConfig(
-        DurationMetric::AggregationType aggregationType) {
-    StatsdConfig config;
-    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-    *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
-    *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
-    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-
-    auto scheduledJobPredicate = CreateScheduledJobPredicate();
-    auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *dimensions = CreateAttributionUidDimensions(
-                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-    dimensions->add_child()->set_field(2);  // job name field.
-
-    auto isSyncingPredicate = CreateIsSyncingPredicate();
-    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidDimensions(
-            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-    syncDimension->add_child()->set_field(2 /* name field*/);
-
-    *config.add_predicate() = scheduledJobPredicate;
-    *config.add_predicate() = isSyncingPredicate;
-
-    auto metric = config.add_duration_metric();
-    metric->set_bucket(FIVE_MINUTES);
-    metric->set_id(StringToId("scheduledJob"));
-    metric->set_what(scheduledJobPredicate.id());
-    metric->set_condition(isSyncingPredicate.id());
-    metric->set_aggregation_type(aggregationType);
-    *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
-            android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-    *metric->mutable_dimensions_in_condition() = *syncDimension;
-
-    auto links = metric->add_links();
-    links->set_condition(isSyncingPredicate.id());
-    *links->mutable_fields_in_what() =
-            CreateAttributionUidDimensions(
-                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-    *links->mutable_fields_in_condition() =
-            CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-    return config;
-}
-
-}  // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition) {
-    for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
-        ConfigKey cfgKey;
-        auto config = createDurationMetric_PartialLink_SimpleConditionConfig(
-                aggregationType);
-        int64_t bucketStartTimeNs = 10000000000;
-        int64_t bucketSizeNs =
-                TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
-        auto processor = CreateStatsLogProcessor(
-                bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
-        EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
-        EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
-        std::vector<AttributionNodeInternal> attributions1 = {
-                CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-                CreateAttribution(222, "GMSCoreModule2")};
-
-        std::vector<AttributionNodeInternal> attributions2 = {
-                CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-                CreateAttribution(555, "GMSCoreModule2")};
-
-        std::vector<AttributionNodeInternal> attributions3 = {
-                CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
-                CreateAttribution(555, "GMSCoreModule2")};
-
-        std::vector<std::unique_ptr<LogEvent>> events;
-
-        events.push_back(CreateStartScheduledJobEvent(
-                {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
-        events.push_back(CreateFinishScheduledJobEvent(
-                {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
-
-        events.push_back(CreateStartScheduledJobEvent(
-                {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
-        events.push_back(CreateFinishScheduledJobEvent(
-                {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
-        events.push_back(CreateStartScheduledJobEvent(
-                {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
-        events.push_back(CreateFinishScheduledJobEvent(
-                {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + bucketSizeNs + 850));
-
-        events.push_back(
-            CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
-                                         bucketStartTimeNs + bucketSizeNs - 2));
-        events.push_back(
-            CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
-                                          bucketStartTimeNs + bucketSizeNs + 900));
-
-        events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                              bucketStartTimeNs + 50));
-        events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                            bucketStartTimeNs + 110));
-
-        events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
-                                              bucketStartTimeNs + 300));
-        events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-                                            bucketStartTimeNs + bucketSizeNs + 700));
-        events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
-                                              bucketStartTimeNs + 400));
-        events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
-                                            bucketStartTimeNs + bucketSizeNs - 1));
-
-        events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
-                                              bucketStartTimeNs + 550));
-        events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
-                                            bucketStartTimeNs + 800));
-        events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
-                                              bucketStartTimeNs + bucketSizeNs - 1));
-        events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
-                                            bucketStartTimeNs + bucketSizeNs + 700));
-
-        sortLogEventsByTimestamp(&events);
-
-        for (const auto& event : events) {
-            processor->OnLogEvent(event.get());
-        }
-
-        ConfigMetricsReportList reports;
-        vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
-                                ADB_DUMP, FAST, &buffer);
-        EXPECT_TRUE(buffer.size() > 0);
-        EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
-        backfillDimensionPath(&reports);
-        backfillStringInReport(&reports);
-        backfillStartEndTimestamp(&reports);
-
-        EXPECT_EQ(reports.reports_size(), 1);
-        EXPECT_EQ(reports.reports(0).metrics_size(), 1);
-        StatsLogReport::DurationMetricDataWrapper metrics;
-        sortMetricDataByDimensionsValue(
-                reports.reports(0).metrics(0).duration_metrics(), &metrics);
-
-        if (aggregationType == DurationMetric::SUM) {
-            EXPECT_EQ(4, metrics.data_size());
-            auto data = metrics.data(0);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
-            EXPECT_EQ("ReadEmail",
-                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
-            EXPECT_EQ(data.bucket_info_size(), 1);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                      bucketStartTimeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                bucketStartTimeNs + bucketSizeNs);
-
-            data = metrics.data(1);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
-            EXPECT_EQ("ReadDoc",
-                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
-            EXPECT_EQ(data.bucket_info_size(), 1);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 1 - 400 - 100);
-
-            data = metrics.data(2);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
-            EXPECT_EQ("ReadEmail",
-                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
-            EXPECT_EQ(data.bucket_info_size(), 2);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300 + bucketSizeNs - 600);
-            EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
-            EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + 2 * bucketSizeNs);
-
-            data = metrics.data(3);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
-            EXPECT_EQ("ReadDoc",
-                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
-            EXPECT_EQ(data.bucket_info_size(), 2);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
-            EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + 2 * bucketSizeNs);
-        } else {
-            EXPECT_EQ(metrics.data_size(), 4);
-            auto data = metrics.data(0);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
-            EXPECT_EQ("ReadEmail",
-                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
-            EXPECT_EQ(data.bucket_info_size(), 1);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                      bucketStartTimeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                bucketStartTimeNs + bucketSizeNs);
-
-            data = metrics.data(1);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
-            EXPECT_EQ("ReadDoc",
-                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
-            EXPECT_EQ(data.bucket_info_size(), 2);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
-            EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + 2 * bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 1 - 600);
-
-            data = metrics.data(2);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
-            EXPECT_EQ("ReadEmail",
-                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
-            EXPECT_EQ(data.bucket_info_size(), 2);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300);
-            EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
-            EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + 2 * bucketSizeNs);
-
-            data = metrics.data(3);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
-            ValidateAttributionUidDimension(
-                data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
-            EXPECT_EQ("ReadDoc",
-                      data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
-            EXPECT_EQ(data.bucket_info_size(), 1);
-            EXPECT_EQ(data.bucket_info(0).duration_nanos(), 701);
-            EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + bucketSizeNs);
-            EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
-                      bucketStartTimeNs + 2 * bucketSizeNs);
-        }
-    }
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 67c704e..839daa4 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -187,9 +187,9 @@
         {getMockedDimensionKey(conditionTagId, 2, "222")};
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse));
+    EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
 
-    EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue));
+    EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
 
     CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
                                       bucketStartTimeNs, bucketStartTimeNs);
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index d2fd95c..f30873b 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -113,9 +113,9 @@
     key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")};
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse));
+    EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
 
-    EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue));
+    EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
 
     EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
 
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index b9553a8..47c21aa 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -485,10 +485,6 @@
     dim->set_field(tagId);
     dim->add_child()->set_field(1);
 
-    dim = metric.mutable_dimensions_in_condition();
-    dim->set_field(conditionTag);
-    dim->add_child()->set_field(1);
-
     UidMap uidMap;
     SimpleAtomMatcher atomMatcher;
     atomMatcher.set_atom_id(tagId);
@@ -496,18 +492,14 @@
         new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    EXPECT_CALL(*wizard, query(_, _, _, _, _, _))
+    EXPECT_CALL(*wizard, query(_, _, _))
             .WillRepeatedly(
                     Invoke([](const int conditionIndex, const ConditionKey& conditionParameters,
-                              const vector<Matcher>& dimensionFields, const bool isSubsetDim,
-                              const bool isPartialLink,
-                              std::unordered_set<HashableDimensionKey>* dimensionKeySet) {
-                        dimensionKeySet->clear();
+                              const bool isPartialLink) {
                         int pos[] = {1, 0, 0};
                         Field f(conditionTag, pos, 0);
                         HashableDimensionKey key;
                         key.mutableValues()->emplace_back(f, Value((int32_t)1000000));
-                        dimensionKeySet->insert(key);
 
                         return ConditionState::kTrue;
                     }));
@@ -538,9 +530,6 @@
     EXPECT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
 
-    EXPECT_EQ(1UL, key.getDimensionKeyInCondition().getValues().size());
-    EXPECT_EQ(1000000, key.getDimensionKeyInCondition().getValues()[0].mValue.int_value);
-
     EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
 
     vector<shared_ptr<LogEvent>> allData;
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index bf04752..100220b 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -52,7 +52,6 @@
     const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
     const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
 
-    vector<Matcher> dimensionInCondition;
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
@@ -63,7 +62,7 @@
     int64_t bucketNum = 0;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1,
                                false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
                                false, false, {});
 
@@ -88,7 +87,6 @@
     const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
     const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
 
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -99,7 +97,7 @@
     int64_t bucketNum = 0;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1,
                                false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
                                false, false, {});
 
@@ -124,7 +122,6 @@
     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
     const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
     const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -135,7 +132,7 @@
     int64_t bucketNum = 0;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1,
                                false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
                                false, false, {});
 
@@ -165,7 +162,6 @@
     const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
     const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
     const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -176,7 +172,7 @@
     int64_t bucketNum = 0;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1,
                                true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
                                false, false, {});
 
@@ -202,7 +198,6 @@
 TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
     const HashableDimensionKey conditionDimKey = key1;
 
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey conditionKey1;
@@ -223,7 +218,7 @@
     int64_t eventStopTimeNs = conditionStops2 + 8 * NS_PER_SEC;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                false, bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, true,
                                false, {});
     EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
@@ -246,7 +241,6 @@
 }
 
 TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey conditionKey1;
@@ -273,7 +267,7 @@
     sp<AlarmMonitor> alarmMonitor;
     sp<DurationAnomalyTracker> anomalyTracker =
         new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
                                true, false, {anomalyTracker});
 
@@ -295,7 +289,6 @@
 // This tests that we correctly compute the predicted time of an anomaly assuming that the current
 // state continues forward as-is.
 TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) {
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey conditionKey1;
@@ -333,7 +326,7 @@
     sp<AlarmMonitor> alarmMonitor;
     sp<DurationAnomalyTracker> anomalyTracker =
         new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
                                true, false, {anomalyTracker});
 
@@ -381,7 +374,6 @@
 // Suppose A starts, then B starts, and then A stops. We still need to set an anomaly based on the
 // elapsed duration of B.
 TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) {
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey conditionKey1;
@@ -416,7 +408,7 @@
     sp<AlarmMonitor> alarmMonitor;
     sp<DurationAnomalyTracker> anomalyTracker =
         new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
                                true, false, {anomalyTracker});
 
@@ -434,4 +426,4 @@
 }  // namespace android
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
\ No newline at end of file
+#endif
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 7c2b423..1cd7bdb 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -51,7 +51,6 @@
 
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -62,7 +61,7 @@
     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
     int64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                  false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
                                  bucketSizeNs, false, false, {});
 
@@ -84,7 +83,6 @@
 
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -94,7 +92,7 @@
     int64_t bucketNum = 0;
     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                  true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
                                  bucketSizeNs, false, false, {});
 
@@ -117,7 +115,6 @@
         {getMockedDimensionKey(TagId, 1, "maps")};
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -127,7 +124,7 @@
     int64_t bucketNum = 0;
     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                  true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
                                  bucketSizeNs, false, false, {});
 
@@ -147,7 +144,6 @@
 
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -158,7 +154,7 @@
     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
     int64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                  true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
                                  bucketSizeNs, false, false, {});
 
@@ -186,13 +182,12 @@
 
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
     key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
-    EXPECT_CALL(*wizard, query(_, key1, _, _, _, _))  // #4
+    EXPECT_CALL(*wizard, query(_, key1, _))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -203,7 +198,7 @@
     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
     int64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                  false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
                                  bucketSizeNs, true, false, {});
 
@@ -224,13 +219,12 @@
 
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
     key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
-    EXPECT_CALL(*wizard, query(_, key1, _, _, _, _))
+    EXPECT_CALL(*wizard, query(_, key1, _))
             .Times(2)
             .WillOnce(Return(ConditionState::kFalse))
             .WillOnce(Return(ConditionState::kTrue));
@@ -243,7 +237,7 @@
     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
     int64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                  false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
                                  bucketSizeNs, true, false, {});
 
@@ -266,13 +260,12 @@
 
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    vector<Matcher> dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
     key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
-    EXPECT_CALL(*wizard, query(_, key1, _, _, _, _))  // #4
+    EXPECT_CALL(*wizard, query(_, key1, _))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
     unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -282,7 +275,7 @@
     int64_t bucketNum = 0;
     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                  true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
                                  bucketSizeNs, true, false, {});
 
@@ -306,7 +299,6 @@
 
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    vector<Matcher> dimensionInCondition;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
@@ -324,7 +316,7 @@
     sp<AlarmMonitor> alarmMonitor;
     sp<DurationAnomalyTracker> anomalyTracker =
         new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                  true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
                                  bucketSizeNs, true, false, {anomalyTracker});
 
@@ -371,7 +363,6 @@
 }
 
 TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp2) {
-    vector<Matcher> dimensionInCondition;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
@@ -387,7 +378,7 @@
     sp<DurationAnomalyTracker> anomalyTracker =
         new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
     OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, wizard, 1,
-                                 dimensionInCondition,
+
                                  true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
                                  bucketSizeNs, true, false, {anomalyTracker});
 
@@ -415,7 +406,7 @@
     for (int j = 0; j < 3; j++) {
         int64_t thresholdNs = j * bucketSizeNs + 5 * NS_PER_SEC;
         for (int i = 0; i <= 7; ++i) {
-            vector<Matcher> dimensionInCondition;
+
             Alert alert;
             alert.set_id(101);
             alert.set_metric_id(1);
@@ -432,7 +423,7 @@
             sp<DurationAnomalyTracker> anomalyTracker =
                 new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
             OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY,
-                                         wizard, 1, dimensionInCondition,
+                                         wizard, 1,
                                          true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
                                          bucketSizeNs, true, false, {anomalyTracker});
 
@@ -472,7 +463,6 @@
 
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    vector<Matcher> dimensionInCondition;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
@@ -491,7 +481,7 @@
     sp<AlarmMonitor> alarmMonitor;
     sp<DurationAnomalyTracker> anomalyTracker =
         new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                  true /*nesting*/, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
                                  bucketSizeNs, false, false, {anomalyTracker});
 
@@ -522,7 +512,6 @@
 
     const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
     const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
-    vector<Matcher> dimensionInCondition;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
@@ -541,7 +530,7 @@
     sp<AlarmMonitor> alarmMonitor;
     sp<DurationAnomalyTracker> anomalyTracker =
         new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1,
                                  true /*nesting*/, bucketStartTimeNs, 0, bucketStartTimeNs,
                                  bucketSizeNs, false, false, {anomalyTracker});
 
@@ -589,4 +578,4 @@
 }  // namespace android
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
\ No newline at end of file
+#endif
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 97c1072..329e39f 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -26,11 +26,9 @@
 
 class MockConditionWizard : public ConditionWizard {
 public:
-    MOCK_METHOD6(query,
+    MOCK_METHOD3(query,
                  ConditionState(const int conditionIndex, const ConditionKey& conditionParameters,
-                                const vector<Matcher>& dimensionFields,
-                                const bool isSubsetDim, const bool isPartialLink,
-                                std::unordered_set<HashableDimensionKey>* dimensionKeySet));
+                                const bool isPartialLink));
 };
 
 class MockStatsPullerManager : public StatsPullerManager {
@@ -55,4 +53,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/config/preloaded-classes b/config/preloaded-classes
index ea50999..778a4d7 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -200,6 +200,7 @@
 android.app.ContextImpl$1
 android.app.ContextImpl$ApplicationContentResolver
 android.app.ContextImpl
+android.app.DeviceIdleFrameworkInitializer
 android.app.DexLoadReporter
 android.app.Dialog$ListenersHandler
 android.app.Dialog
diff --git a/config/preloaded-classes-extra b/config/preloaded-classes-extra
index 94849fb..4bfa873 100644
--- a/config/preloaded-classes-extra
+++ b/config/preloaded-classes-extra
@@ -1,5 +1,6 @@
 # JobSchedulerFrameworkInitializer must always be preloaded because it registers the job scheduler
 # service wrapper to SystemServiceRegistry.
+android.app.DeviceIdleFrameworkInitializer
 android.app.job.JobSchedulerFrameworkInitializer
 android.icu.impl.coll.CollationRoot
 android.icu.impl.IDNA2003
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d20cc41..34045c9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.Manifest;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -29,6 +30,7 @@
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes.AttributeUsage;
 import android.os.Binder;
@@ -1095,21 +1097,27 @@
             "android:sms_financial_transactions";
 
     /** @hide Read media of audio type. */
+    @SystemApi @TestApi
     public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
     /** @hide Write media of audio type. */
+    @SystemApi @TestApi
     public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio";
     /** @hide Read media of video type. */
+    @SystemApi @TestApi
     public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
     /** @hide Write media of video type. */
+    @SystemApi @TestApi
     public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video";
     /** @hide Read media of image type. */
+    @SystemApi @TestApi
     public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
     /** @hide Write media of image type. */
+    @SystemApi @TestApi
     public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images";
     /** @hide Has a legacy (non-isolated) view of storage. */
-    @TestApi
-    @SystemApi
+    @SystemApi @TestApi
     public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
+
     /** @hide Interact with accessibility. */
     @SystemApi
     public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
@@ -4281,20 +4289,17 @@
 
     /**
      * Callback for notification of changes to operation active state.
-     *
-     * @hide
      */
-    @TestApi
     public interface OnOpActiveChangedListener {
         /**
          * Called when the active state of an app op changes.
          *
-         * @param code The op code.
-         * @param uid The UID performing the operation.
+         * @param op The operation that changed.
          * @param packageName The package performing the operation.
          * @param active Whether the operation became active or inactive.
          */
-        void onOpActiveChanged(int code, int uid, String packageName, boolean active);
+        void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
+                boolean active);
     }
 
     /**
@@ -4324,6 +4329,16 @@
         public void onOpChanged(int op, String packageName) { }
     }
 
+    /**
+     * Callback for notification of changes to operation state.
+     * This allows you to see the raw op codes instead of strings.
+     * @hide
+     */
+    public interface OnOpActiveChangedInternalListener extends OnOpActiveChangedListener {
+        default void onOpActiveChanged(String op, int uid, String packageName, boolean active) { }
+        default void onOpActiveChanged(int op, int uid, String packageName, boolean active) { }
+    }
+
     AppOpsManager(Context context, IAppOpsService service) {
         mContext = context;
         mService = service;
@@ -4779,6 +4794,17 @@
         }
     }
 
+    /** {@hide} */
+    @Deprecated
+    public void startWatchingActive(@NonNull int[] ops,
+            @NonNull OnOpActiveChangedListener callback) {
+        final String[] strOps = new String[ops.length];
+        for (int i = 0; i < ops.length; i++) {
+            strOps[i] = opToPublicName(ops[i]);
+        }
+        startWatchingActive(strOps, mContext.getMainExecutor(), callback);
+    }
+
     /**
      * Start watching for changes to the active state of app ops. An app op may be
      * long running and it has a clear start and stop delimiters. If an op is being
@@ -4786,26 +4812,25 @@
      * watched ops for a registered callback you need to unregister and register it
      * again.
      *
-     * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+     * <p> If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS} permission
      * you can watch changes only for your UID.
      *
-     * @param ops The ops to watch.
+     * @param ops The operations to watch.
      * @param callback Where to report changes.
      *
-     * @see #isOperationActive(int, int, String)
-     * @see #stopWatchingActive(OnOpActiveChangedListener)
+     * @see #isOperationActive
+     * @see #stopWatchingActive
      * @see #startOp(int, int, String)
      * @see #finishOp(int, int, String)
-     *
-     * @hide
      */
-    @TestApi
     // TODO: Uncomment below annotation once b/73559440 is fixed
     // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
-    public void startWatchingActive(@NonNull int[] ops,
+    public void startWatchingActive(@NonNull String[] ops,
+            @CallbackExecutor @NonNull Executor executor,
             @NonNull OnOpActiveChangedListener callback) {
-        Preconditions.checkNotNull(ops, "ops cannot be null");
-        Preconditions.checkNotNull(callback, "callback cannot be null");
+        Objects.requireNonNull(ops);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
         IAppOpsActiveCallback cb;
         synchronized (mActiveWatchers) {
             cb = mActiveWatchers.get(callback);
@@ -4815,13 +4840,25 @@
             cb = new IAppOpsActiveCallback.Stub() {
                 @Override
                 public void opActiveChanged(int op, int uid, String packageName, boolean active) {
-                    callback.onOpActiveChanged(op, uid, packageName, active);
+                    executor.execute(() -> {
+                        if (callback instanceof OnOpActiveChangedInternalListener) {
+                            ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
+                                    uid, packageName, active);
+                        }
+                        if (sOpToString[op] != null) {
+                            callback.onOpActiveChanged(sOpToString[op], uid, packageName, active);
+                        }
+                    });
                 }
             };
             mActiveWatchers.put(callback, cb);
         }
+        final int[] rawOps = new int[ops.length];
+        for (int i = 0; i < ops.length; i++) {
+            rawOps[i] = strOpToOp(ops[i]);
+        }
         try {
-            mService.startWatchingActive(ops, cb);
+            mService.startWatchingActive(rawOps, cb);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4832,14 +4869,11 @@
      * long running and it has a clear start and stop delimiters. Unregistering a
      * non-registered callback has no effect.
      *
-     * @see #isOperationActive#(int, int, String)
-     * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+     * @see #isOperationActive
+     * @see #startWatchingActive
      * @see #startOp(int, int, String)
      * @see #finishOp(int, int, String)
-     *
-     * @hide
      */
-    @TestApi
     public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
         synchronized (mActiveWatchers) {
             final IAppOpsActiveCallback cb = mActiveWatchers.remove(callback);
@@ -5449,6 +5483,19 @@
     }
 
     /**
+     * Checks whether the given op for a package is active.
+     * <p>
+     * If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS}
+     * permission you can query only for your UID.
+     *
+     * @see #finishOp(int)
+     * @see #startOp(int)
+     */
+    public boolean isOpActive(@NonNull String op, int uid, @NonNull String packageName) {
+        return isOperationActive(strOpToOp(op), uid, packageName);
+    }
+
+    /**
      * Checks whether the given op for a UID and package is active.
      *
      * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
diff --git a/core/java/android/app/DeviceIdleFrameworkInitializer.java b/core/java/android/app/DeviceIdleFrameworkInitializer.java
new file mode 100644
index 0000000..b304afb
--- /dev/null
+++ b/core/java/android/app/DeviceIdleFrameworkInitializer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.Context;
+import android.os.DeviceIdleManager;
+import android.os.IDeviceIdleController;
+
+/**
+ * This class needs to be pre-loaded by zygote.  This is where the device idle manager wrapper
+ * is registered.
+ *
+ * @hide
+ */
+public class DeviceIdleFrameworkInitializer {
+    static {
+        SystemServiceRegistry.registerCachedService(
+                Context.DEVICE_IDLE_CONTROLLER, DeviceIdleManager.class,
+                (context, b) -> new DeviceIdleManager(
+                        context.getOuterContext(), IDeviceIdleController.Stub.asInterface(b)));
+    }
+}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index c49ea09..8987b23 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -883,7 +883,7 @@
             }
         }
 
-        // /aepx/com.android.runtime/lib, /vendor/lib, /odm/lib and /product/lib
+        // /apex/com.android.art/lib, /vendor/lib, /odm/lib and /product/lib
         // are added to the native lib search paths of the classloader.
         // Note that this is done AFTER the classloader is
         // created by ApplicationLoaders.getDefault().getClassLoader(...). The
@@ -904,8 +904,8 @@
         // (linker namespace).
         List<String> extraLibPaths = new ArrayList<>(4);
         String abiSuffix = VMRuntime.getRuntime().is64Bit() ? "64" : "";
-        if (!defaultSearchPaths.contains("/apex/com.android.runtime/lib")) {
-            extraLibPaths.add("/apex/com.android.runtime/lib" + abiSuffix);
+        if (!defaultSearchPaths.contains("/apex/com.android.art/lib")) {
+            extraLibPaths.add("/apex/com.android.art/lib" + abiSuffix);
         }
         if (!defaultSearchPaths.contains("/vendor/lib")) {
             extraLibPaths.add("/vendor/lib" + abiSuffix);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 635b9b0..e4fd566 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -127,12 +127,10 @@
 import android.os.BatteryStats;
 import android.os.BugreportManager;
 import android.os.Build;
-import android.os.DeviceIdleManager;
 import android.os.DropBoxManager;
 import android.os.HardwarePropertiesManager;
 import android.os.IBatteryPropertiesRegistrar;
 import android.os.IBinder;
-import android.os.IDeviceIdleController;
 import android.os.IDumpstate;
 import android.os.IHardwarePropertiesManager;
 import android.os.IPowerManager;
@@ -195,6 +193,7 @@
 import com.android.internal.policy.PhoneLayoutInflater;
 
 import java.util.Map;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 
 /**
@@ -1219,17 +1218,6 @@
                     }
             });
 
-        registerService(Context.DEVICE_IDLE_CONTROLLER, DeviceIdleManager.class,
-                new CachedServiceFetcher<DeviceIdleManager>() {
-                    @Override
-                    public DeviceIdleManager createService(ContextImpl ctx)
-                            throws ServiceNotFoundException {
-                        IDeviceIdleController service = IDeviceIdleController.Stub.asInterface(
-                                ServiceManager.getServiceOrThrow(
-                                        Context.DEVICE_IDLE_CONTROLLER));
-                        return new DeviceIdleManager(ctx.getOuterContext(), service);
-                    }});
-
         registerService(Context.TIME_DETECTOR_SERVICE, TimeDetector.class,
                 new CachedServiceFetcher<TimeDetector>() {
                     @Override
@@ -1330,9 +1318,9 @@
      *
      * @hide
      */
-    public static <T> void registerStaticService(String serviceName, Class<T> serviceClass,
+    public static <T> void registerStaticService(String serviceName, Class<T> serviceWrapperClass,
             Function<IBinder, T> serviceFetcher) {
-        registerService(serviceName, serviceClass,
+        registerService(serviceName, serviceWrapperClass,
                 new StaticServiceFetcher<T>() {
                     @Override
                     public T createService() throws ServiceNotFoundException {
@@ -1342,6 +1330,22 @@
     }
 
     /**
+     * APEX modules will use it to register their service wrapper.
+     *
+     * @hide
+     */
+    public static <T> void registerCachedService(String serviceName, Class<T> serviceWrapperClass,
+            BiFunction<ContextImpl, IBinder, T> serviceFetcher) {
+        registerService(serviceName, serviceWrapperClass,
+                new CachedServiceFetcher<T>() {
+                    @Override
+                    public T createService(ContextImpl ctx) throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getServiceOrThrow(serviceName);
+                        return serviceFetcher.apply(ctx, b);
+                    }});
+    }
+
+    /**
      * Base interface for classes that fetch services.
      * These objects must only be created during static initialization.
      */
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index 29acd5d..0c6a935 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -64,6 +64,16 @@
     public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
 
     /**
+     * The MIME type for data whose type is otherwise unknown.
+     * <p>
+     * Per RFC 2046, the "application" media type is to be used for discrete
+     * data which do not fit in any of the other categories, and the
+     * "octet-stream" subtype is used to indicate that a body contains arbitrary
+     * binary data.
+     */
+    public static final String MIMETYPE_UNKNOWN = "application/octet-stream";
+
+    /**
      * The name of the extra used to define a component name when copying/dragging
      * an app icon from Launcher.
      * <p>
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9c86359..1f48393 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -475,11 +475,9 @@
      */
     public static final String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
 
-    /**
-     * Default MIME type for files whose type is otherwise unknown.
-     * @hide
-     */
-    public static final String MIME_TYPE_DEFAULT = "application/octet-stream";
+    /** {@hide} */
+    @Deprecated
+    public static final String MIME_TYPE_DEFAULT = ClipDescription.MIMETYPE_UNKNOWN;
 
     /** @hide */
     @UnsupportedAppUsage
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index 8223a0b..bdd1f4c 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -259,8 +259,6 @@
      * Indicates whether this collection is empty.
      *
      * @return true iff size == 0
-     * {@hide}
-     * TODO: consider exposing this new method publicly
      */
     public boolean isEmpty() {
         return mMap.isEmpty();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2c53faa..a3e940b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4190,6 +4190,7 @@
      * @see #getSystemService(String)
      * @hide
      */
+    @TestApi
     public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
 
     /**
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index fcdc81c..d03bea2 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -328,6 +328,14 @@
     }
 
     /** @hide */
+    public String toFullString() {
+        return "UserInfo[id=" + id
+                + ", name=" + name
+                + ", flags=" + flagsToString(flags)
+                + "]";
+    }
+
+    /** @hide */
     public static String flagsToString(int flags) {
         return DebugUtils.flagsToString(UserInfo.class, "FLAG_", flags);
     }
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index 1b28d61..a4c65ae 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -28,7 +28,6 @@
 import android.opengl.GLES11Ext;
 import android.opengl.GLES20;
 import android.opengl.Matrix;
-import android.text.format.Time;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Size;
@@ -39,9 +38,14 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.FloatBuffer;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * A renderer class that manages the GL state, and can draw a frame into a set of output
@@ -63,6 +67,9 @@
     private static final int FLIP_TYPE_VERTICAL = 2;
     private static final int FLIP_TYPE_BOTH = FLIP_TYPE_HORIZONTAL | FLIP_TYPE_VERTICAL;
 
+    private static final DateTimeFormatter LOG_NAME_TIME_FORMATTER =
+            DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss", Locale.ROOT);
+
     private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
     private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
     private EGLConfig mConfigs;
@@ -624,9 +631,7 @@
         path.append(File.separator);
         path.append("durations_");
 
-        Time now = new Time();
-        now.setToNow();
-        path.append(now.format2445());
+        path.append(formatTimestamp(System.currentTimeMillis()));
         path.append("_S");
         for (EGLSurfaceHolder surface : mSurfaces) {
             path.append(String.format("_%d_%d", surface.width, surface.height));
@@ -639,6 +644,15 @@
         mPerfMeasurer.dumpPerformanceData(path.toString());
     }
 
+    private static String formatTimestamp(long timeMillis) {
+        // This is a replacement for {@link Time#format2445()} that doesn't suffer from Y2038
+        // issues.
+        Instant instant = Instant.ofEpochMilli(timeMillis);
+        ZoneId zoneId = ZoneId.systemDefault();
+        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zoneId);
+        return LOG_NAME_TIME_FORMATTER.format(localDateTime);
+    }
+
     private void setupGlTiming() {
         if (PerfMeasurement.isGlTimingSupported()) {
             Log.d(TAG, "Enabling GL performance measurement");
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 3462d1f..6d5fe53b 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -168,8 +168,11 @@
         return DIR_ANDROID_ROOT;
     }
 
-    /** {@hide} */
-    @TestApi
+    /**
+     * Return root directory where all external storage devices will be mounted.
+     * For example, {@link #getExternalStorageDirectory()} will appear under
+     * this location.
+     */
     public static @NonNull File getStorageDirectory() {
         return DIR_ANDROID_STORAGE;
     }
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 4f7c8c5..bc03e51 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -706,16 +706,11 @@
         synchronized (sLock) {
             for (int i = 0; i < sListeners.size(); i++) {
                 if (namespace.equals(sListeners.valueAt(i).first)) {
-                    final int j = i;
-                    sListeners.valueAt(i).second.execute(new Runnable() {
-                        @Override
-                        public void run() {
-                            Map<String, String> propertyMap = new HashMap(1);
-                            propertyMap.put(name, value);
-                            sListeners.keyAt(j)
-                                    .onPropertiesChanged(new Properties(namespace, propertyMap));
-                        }
-
+                    final OnPropertiesChangedListener listener = sListeners.keyAt(i);
+                    sListeners.valueAt(i).second.execute(() -> {
+                        Map<String, String> propertyMap = new ArrayMap<>(1);
+                        propertyMap.put(name, value);
+                        listener.onPropertiesChanged(new Properties(namespace, propertyMap));
                     });
                 }
             }
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index a959913f..079a42d 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -553,7 +553,7 @@
      * By default no pending items are returned.
      *
      * @see MediaColumns#IS_PENDING
-     * @see MediaStore#setIncludePending(Uri)
+     * @see MediaStore#getIncludePending(Uri)
      */
     public static @NonNull Uri setIncludePending(@NonNull Uri uri) {
         return setIncludePending(uri.buildUpon()).build();
@@ -565,6 +565,17 @@
     }
 
     /**
+     * Return if any pending media items should be included in calls such as
+     * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
+     *
+     * @see MediaColumns#IS_PENDING
+     * @see MediaStore#setIncludePending(Uri)
+     */
+    public static boolean getIncludePending(@NonNull Uri uri) {
+        return parseBoolean(uri.getQueryParameter(MediaStore.PARAM_INCLUDE_PENDING));
+    }
+
+    /**
      * Update the given {@link Uri} to also include any trashed media items from
      * calls such as
      * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
@@ -594,12 +605,24 @@
      * {@link UnsupportedOperationException} will be thrown when the returned
      * {@link Uri} is used, such as when the caller doesn't hold
      * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION}.
+     *
+     * @see MediaStore#getRequireOriginal(Uri)
      */
     public static @NonNull Uri setRequireOriginal(@NonNull Uri uri) {
         return uri.buildUpon().appendQueryParameter(PARAM_REQUIRE_ORIGINAL, "1").build();
     }
 
     /**
+     * Return if the caller requires the original file contents when calling
+     * {@link ContentResolver#openFileDescriptor(Uri, String)}.
+     *
+     * @see MediaStore#setRequireOriginal(Uri)
+     */
+    public static boolean getRequireOriginal(@NonNull Uri uri) {
+        return parseBoolean(uri.getQueryParameter(MediaStore.PARAM_REQUIRE_ORIGINAL));
+    }
+
+    /**
      * Create a new pending media item using the given parameters. Pending items
      * are expected to have a short lifetime, and owners should either
      * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a
@@ -666,45 +689,11 @@
                     (System.currentTimeMillis() + DateUtils.DAY_IN_MILLIS) / 1000);
         }
 
-        /**
-         * Optionally set the primary directory under which this pending item
-         * should be persisted. Only specific well-defined directories from
-         * {@link Environment} are allowed based on the media type being
-         * inserted.
-         * <p>
-         * For example, when creating pending {@link MediaStore.Images.Media}
-         * items, only {@link Environment#DIRECTORY_PICTURES} or
-         * {@link Environment#DIRECTORY_DCIM} are allowed.
-         * <p>
-         * You may leave this value undefined to store the media in a default
-         * location. For example, when this value is left undefined, pending
-         * {@link MediaStore.Audio.Media} items are stored under
-         * {@link Environment#DIRECTORY_MUSIC}.
-         *
-         * @see MediaColumns#PRIMARY_DIRECTORY
-         */
-        public void setPrimaryDirectory(@Nullable String primaryDirectory) {
-            if (primaryDirectory == null) {
-                this.insertValues.remove(MediaColumns.PRIMARY_DIRECTORY);
+        public void setRelativePath(@Nullable String relativePath) {
+            if (relativePath == null) {
+                this.insertValues.remove(MediaColumns.RELATIVE_PATH);
             } else {
-                this.insertValues.put(MediaColumns.PRIMARY_DIRECTORY, primaryDirectory);
-            }
-        }
-
-        /**
-         * Optionally set the secondary directory under which this pending item
-         * should be persisted. Any valid directory name is allowed.
-         * <p>
-         * You may leave this value undefined to store the media as a direct
-         * descendant of the {@link #setPrimaryDirectory(String)} location.
-         *
-         * @see MediaColumns#SECONDARY_DIRECTORY
-         */
-        public void setSecondaryDirectory(@Nullable String secondaryDirectory) {
-            if (secondaryDirectory == null) {
-                this.insertValues.remove(MediaColumns.SECONDARY_DIRECTORY);
-            } else {
-                this.insertValues.put(MediaColumns.SECONDARY_DIRECTORY, secondaryDirectory);
+                this.insertValues.put(MediaColumns.RELATIVE_PATH, relativePath);
             }
         }
 
@@ -1470,7 +1459,14 @@
                     .appendPath("downloads").build();
         }
 
-        /** @hide */
+        /**
+         * Get the content:// style URI for a single row in the downloads table
+         * on the given volume.
+         *
+         * @param volumeName the name of the volume to get the URI for
+         * @param id the download to get the URI for
+         * @return the URI to the downloads table on the given volume
+         */
         public static @NonNull Uri getContentUri(@NonNull String volumeName, long id) {
             return ContentUris.withAppendedId(getContentUri(volumeName), id);
         }
@@ -1795,7 +1791,14 @@
                         .appendPath("media").build();
             }
 
-            /** @hide */
+            /**
+             * Get the content:// style URI for a single row in the images table
+             * on the given volume.
+             *
+             * @param volumeName the name of the volume to get the URI for
+             * @param id the image to get the URI for
+             * @return the URI to the images table on the given volume
+             */
             public static @NonNull Uri getContentUri(@NonNull String volumeName, long id) {
                 return ContentUris.withAppendedId(getContentUri(volumeName), id);
             }
@@ -2282,7 +2285,14 @@
                         .appendPath("media").build();
             }
 
-            /** @hide */
+            /**
+             * Get the content:// style URI for a single row in the audio table
+             * on the given volume.
+             *
+             * @param volumeName the name of the volume to get the URI for
+             * @param id the audio to get the URI for
+             * @return the URI to the audio table on the given volume
+             */
             public static @NonNull Uri getContentUri(@NonNull String volumeName, long id) {
                 return ContentUris.withAppendedId(getContentUri(volumeName), id);
             }
@@ -3031,7 +3041,14 @@
                         .appendPath("media").build();
             }
 
-            /** @hide */
+            /**
+             * Get the content:// style URI for a single row in the videos table
+             * on the given volume.
+             *
+             * @param volumeName the name of the volume to get the URI for
+             * @param id the video to get the URI for
+             * @return the URI to the videos table on the given volume
+             */
             public static @NonNull Uri getContentUri(@NonNull String volumeName, long id) {
                 return ContentUris.withAppendedId(getContentUri(volumeName), id);
             }
@@ -3296,6 +3313,13 @@
         return volumeName;
     }
 
+    private static boolean parseBoolean(@Nullable String value) {
+        if (value == null) return false;
+        if ("1".equals(value)) return true;
+        if ("true".equalsIgnoreCase(value)) return true;
+        return false;
+    }
+
     /**
      * Return path where the given specific volume is mounted. Not valid for
      * {@link #VOLUME_INTERNAL} or {@link #VOLUME_EXTERNAL}, since those are
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 30c4e90..5143f18 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -51,7 +51,6 @@
 
 import com.android.internal.util.Preconditions;
 
-import java.lang.ref.WeakReference;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -431,23 +430,18 @@
      * Forwards the callback result to a wrapped binder callback.
      */
     private static final class ProxyCallback<T extends Parcelable> implements Callback<T> {
-        private WeakReference<ITextClassifierCallback> mTextClassifierCallback;
+        private ITextClassifierCallback mTextClassifierCallback;
 
         private ProxyCallback(ITextClassifierCallback textClassifierCallback) {
-            mTextClassifierCallback =
-                    new WeakReference<>(Preconditions.checkNotNull(textClassifierCallback));
+            mTextClassifierCallback = Preconditions.checkNotNull(textClassifierCallback);
         }
 
         @Override
         public void onSuccess(T result) {
-            ITextClassifierCallback callback = mTextClassifierCallback.get();
-            if (callback == null) {
-                return;
-            }
             try {
                 Bundle bundle = new Bundle(1);
                 bundle.putParcelable(KEY_RESULT, result);
-                callback.onSuccess(bundle);
+                mTextClassifierCallback.onSuccess(bundle);
             } catch (RemoteException e) {
                 Slog.d(LOG_TAG, "Error calling callback");
             }
@@ -455,12 +449,9 @@
 
         @Override
         public void onFailure(CharSequence error) {
-            ITextClassifierCallback callback = mTextClassifierCallback.get();
-            if (callback == null) {
-                return;
-            }
             try {
-                callback.onFailure();
+                Slog.w(LOG_TAG, "Request fail: " + error);
+                mTextClassifierCallback.onFailure();
             } catch (RemoteException e) {
                 Slog.d(LOG_TAG, "Error calling callback");
             }
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index a97c330..66f7504 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -309,7 +309,7 @@
         }
 
         public void onFailure() {
-            Log.e(LOG_TAG, "Request failed.", null);
+            Log.e(LOG_TAG, "Request failed at " + mName, null);
             mLatch.countDown();
         }
 
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 51ca805..d0f8093 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -999,6 +999,7 @@
         }
 
         private void onTimeOut() {
+            Log.d(LOG_TAG, "Timeout in TextClassificationAsyncTask");
             if (getStatus() == Status.RUNNING) {
                 onPostExecute(mTimeOutResultSupplier.get());
             }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 96bb87b..4c54539 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -339,6 +339,8 @@
     cppflags: ["-Wno-conversion-null"],
 
     srcs: [
+        "android/graphics/apex/android_region.cpp",
+
         "android_graphics_Canvas.cpp",
         "android_graphics_ColorSpace.cpp",
         "android_graphics_drawable_AnimatedVectorDrawable.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index a3fd915..c12940a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -245,6 +245,9 @@
 // Copying (CC) garbage collector.
 static const char* kNoGenerationalCCRuntimeOption = "-Xgc:nogenerational_cc";
 
+// Phenotype property name for enabling profiling the boot class path.
+static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
+
 // Feature flag name for running the JIT in Zygote experiment, b/119800099.
 static const char* ENABLE_APEX_IMAGE = "enable_apex_image";
 // Flag to pass to the runtime when using the apex image.
@@ -690,6 +693,24 @@
     char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX];
     char bootImageBuf[sizeof("-Ximage:") - 1 + PROPERTY_VALUE_MAX];
 
+    // Read if we are using the profile configuration, do this at the start since the last ART args
+    // take precedence.
+    property_get("dalvik.vm.profilebootclasspath", propBuf, "");
+    std::string profile_boot_class_path = propBuf;
+    // Empty means the property is unset and we should default to the phenotype property.
+    // The possible values are {"true", "false", ""}
+    if (profile_boot_class_path.empty()) {
+        profile_boot_class_path = server_configurable_flags::GetServerConfigurableFlag(
+                RUNTIME_NATIVE_BOOT_NAMESPACE,
+                PROFILE_BOOT_CLASS_PATH,
+                /*default_value=*/ "");
+    }
+    if (profile_boot_class_path == "true") {
+        addOption("-Xps-profile-boot-class-path");
+        addOption("-Xps-profile-aot-code");
+        addOption("-Xjitsaveprofilinginfo");
+    }
+
     std::string use_apex_image =
         server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
                                                              ENABLE_APEX_IMAGE,
@@ -807,13 +828,6 @@
     parseRuntimeOption("dalvik.vm.jittransitionweight",
                        jittransitionweightOptBuf,
                        "-Xjittransitionweight:");
-
-    property_get("dalvik.vm.profilebootimage", propBuf, "");
-    if (strcmp(propBuf, "true") == 0) {
-        addOption("-Xps-profile-boot-class-path");
-        addOption("-Xps-profile-aot-code");
-    }
-
     /*
      * Madvise related options.
      */
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index 90ca3bb..1fb5fe3 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -360,8 +360,4 @@
                                 NELEM(gRegionIterMethods));
 }
 
-SkRegion* android_graphics_Region_getSkRegion(JNIEnv* env, jobject regionObj) {
-    return GetSkRegion(env, regionObj);
-}
-
 } // namespace android
diff --git a/core/jni/android/graphics/Region.h b/core/jni/android/graphics/Region.h
deleted file mode 100644
index 2e8e109..0000000
--- a/core/jni/android/graphics/Region.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROID_GRAPHICS_REGION_H_
-#define _ANDROID_GRAPHICS_REGION_H_
-
-#include "jni.h"
-#include "SkRegion.h"
-
-namespace android {
-
-/* Gets the underlying SkRegion from a Region object. */
-extern SkRegion* android_graphics_Region_getSkRegion(JNIEnv* env, jobject regionObj);
-
-} // namespace android
-
-#endif // _ANDROID_GRAPHICS_REGION_H_
diff --git a/core/jni/android/graphics/apex/android_region.cpp b/core/jni/android/graphics/apex/android_region.cpp
new file mode 100644
index 0000000..2030e7e
--- /dev/null
+++ b/core/jni/android/graphics/apex/android_region.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android/graphics/region.h"
+
+#include "GraphicsJNI.h"
+
+#include <SkRegion.h>
+
+static inline SkRegion::Iterator* ARegionIter_to_SkRegionIter(ARegionIterator* iterator) {
+    return reinterpret_cast<SkRegion::Iterator*>(iterator);
+}
+
+static inline ARegionIterator* SkRegionIter_to_ARegionIter(SkRegion::Iterator* iterator) {
+    return reinterpret_cast<ARegionIterator*>(iterator);
+}
+
+ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject regionObj) {
+    SkRegion* region = GraphicsJNI::getNativeRegion(env, regionObj);
+    return (!region) ? nullptr : SkRegionIter_to_ARegionIter(new SkRegion::Iterator(*region));
+}
+
+void ARegionIterator_releaseIterator(ARegionIterator* iterator) {
+    delete ARegionIter_to_SkRegionIter(iterator);
+}
+
+bool ARegionIterator_isComplex(ARegionIterator* iterator) {
+    return ARegionIter_to_SkRegionIter(iterator)->rgn()->isComplex();
+}
+
+bool ARegionIterator_isDone(ARegionIterator* iterator) {
+    return ARegionIter_to_SkRegionIter(iterator)->done();
+}
+
+void ARegionIterator_next(ARegionIterator* iterator) {
+    ARegionIter_to_SkRegionIter(iterator)->next();
+}
+
+ARect ARegionIterator_getRect(ARegionIterator* iterator) {
+    const SkIRect& rect = ARegionIter_to_SkRegionIter(iterator)->rect();
+    return { rect.fLeft, rect.fTop, rect.fRight, rect.fBottom };
+}
+
+ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator) {
+    const SkIRect& bounds = ARegionIter_to_SkRegionIter(iterator)->rgn()->getBounds();
+    return { bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom };
+}
diff --git a/core/jni/android/graphics/apex/include/android/graphics/region.h b/core/jni/android/graphics/apex/include/android/graphics/region.h
new file mode 100644
index 0000000..961067a
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/region.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_GRAPHICS_REGION_H
+#define ANDROID_GRAPHICS_REGION_H
+
+#include <android/rect.h>
+#include <sys/cdefs.h>
+#include <jni.h>
+
+__BEGIN_DECLS
+
+/**
+* Opaque handle for a native graphics region iterator.
+*/
+typedef struct ARegionIterator ARegionIterator;
+
+/**
+ * Returns a iterator for a Java android.graphics.Region
+ *
+ * @param env
+ * @param region
+ * @return ARegionIterator that must be closed and must not live longer than the life
+ *         of the jobject.  It returns nullptr if the region is not a valid object.
+ */
+ARegionIterator* ARegionIterator_acquireIterator(JNIEnv* env, jobject region);
+
+void ARegionIterator_releaseIterator(ARegionIterator* iterator);
+
+bool ARegionIterator_isComplex(ARegionIterator* iterator);
+
+bool ARegionIterator_isDone(ARegionIterator* iterator);
+
+void ARegionIterator_next(ARegionIterator* iterator);
+
+ARect ARegionIterator_getRect(ARegionIterator* iterator);
+
+ARect ARegionIterator_getTotalBounds(ARegionIterator* iterator);
+
+__END_DECLS
+
+#ifdef	__cplusplus
+namespace android {
+namespace graphics {
+    class RegionIterator {
+    public:
+        RegionIterator(JNIEnv* env, jobject region)
+                : mIterator(ARegionIterator_acquireIterator(env, region)) {}
+        ~RegionIterator() { ARegionIterator_releaseIterator(mIterator); }
+
+        bool isValid() const { return mIterator != nullptr; }
+        bool isComplex() { return ARegionIterator_isComplex(mIterator); }
+        bool isDone() { return ARegionIterator_isDone(mIterator); }
+        void next() { ARegionIterator_next(mIterator); }
+        ARect getRect() { return ARegionIterator_getRect(mIterator); }
+        ARect getTotalBounds() const { return ARegionIterator_getTotalBounds(mIterator); }
+    private:
+        ARegionIterator* mIterator;
+    };
+}; // namespace graphics
+}; // namespace android
+
+#endif // __cplusplus
+#endif // ANDROID_GRAPHICS_REGION_H
\ No newline at end of file
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 98680fa1e..64caf33 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -22,7 +22,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/threads.h>
 
-#include <android/graphics/Region.h>
+#include <android/graphics/region.h>
 #include <gui/SurfaceControl.h>
 #include <ui/Region.h>
 
@@ -128,10 +128,9 @@
     jobject regionObj = env->GetObjectField(obj,
             gInputWindowHandleClassInfo.touchableRegion);
     if (regionObj) {
-        SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj);
-        for (SkRegion::Iterator it(*region); !it.done(); it.next()) {
-            const SkIRect& rect = it.rect();
-            mInfo.addTouchableRegion(Rect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom));
+        for (graphics::RegionIterator it(env, regionObj); !it.isDone(); it.next()) {
+            ARect rect = it.getRect();
+            mInfo.addTouchableRegion(Rect(rect.left, rect.top, rect.right, rect.bottom));
         }
         env->DeleteLocalRef(regionObj);
     }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 096bf69..67f52f4 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -20,9 +20,9 @@
 #include "android_os_Parcel.h"
 #include "android_util_Binder.h"
 #include "android_hardware_input_InputWindowHandle.h"
-#include "android/graphics/Region.h"
 #include "core_jni_helpers.h"
 
+#include <android/graphics/region.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android-base/chrono_utils.h>
 #include <nativehelper/JNIHelp.h>
@@ -425,20 +425,19 @@
 static void nativeSetTransparentRegionHint(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject, jobject regionObj) {
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj);
-    if (!region) {
+    graphics::RegionIterator iterator(env, regionObj);
+    if (!iterator.isValid()) {
         doThrowIAE(env);
         return;
     }
 
-    const SkIRect& b(region->getBounds());
-    Region reg(Rect(b.fLeft, b.fTop, b.fRight, b.fBottom));
-    if (region->isComplex()) {
-        SkRegion::Iterator it(*region);
-        while (!it.done()) {
-            const SkIRect& r(it.rect());
-            reg.addRectUnchecked(r.fLeft, r.fTop, r.fRight, r.fBottom);
-            it.next();
+    ARect bounds = iterator.getTotalBounds();
+    Region reg({bounds.left, bounds.top, bounds.right, bounds.bottom});
+    if (iterator.isComplex()) {
+        while (!iterator.isDone()) {
+            ARect rect = iterator.getRect();
+            reg.addRectUnchecked(rect.left, rect.top, rect.right, rect.bottom);
+            iterator.next();
         }
     }
 
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index ea4b252..bb57805 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -80,9 +80,9 @@
     return true;
   }
 
-  // Jars from the runtime apex are allowed.
-  static const char* kRuntimeApexPrefix = "/apex/com.android.runtime/javalib/";
-  if (android::base::StartsWith(path, kRuntimeApexPrefix)
+  // Jars from the ART APEX are allowed.
+  static const char* kArtApexPrefix = "/apex/com.android.art/javalib/";
+  if (android::base::StartsWith(path, kArtApexPrefix)
       && android::base::EndsWith(path, kJarSuffix)) {
     return true;
   }
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2067735..96c0cf3 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2927,7 +2927,6 @@
     <public type="attr" name="enforceStatusBarContrast" id="0x01010604" />
     <public type="attr" name="enforceNavigationBarContrast" id="0x01010605" />
     <public type="attr" name="identifier" id="0x01010606" />
-    <public type="attr" name="forceQueryable" id="0x01010608" />
 
     <!-- @hide @SystemApi -->
     <public type="drawable" name="ic_info" id="0x010800b4" />
@@ -3000,6 +2999,7 @@
 
     <public-group type="attr" first-id="0x01010607">
       <public name="importantForContentCapture" />
+      <public name="forceQueryable" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b5">
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index b9945cc..e6eaa696 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -207,7 +207,7 @@
     }
 
     public GradientDrawable() {
-        this(new GradientState(Orientation.TOP_BOTTOM, null), null);
+        this(new GradientState(Orientation.LEFT_RIGHT, null), null);
     }
 
     /**
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index db63889..b15cc5c 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -24,6 +24,5 @@
     srcs_lib: "framework-minus-apex",
     // TODO(b/70046217): remove core/java and android below. It was added to provide definitions for
     // types like android.os.Bundle
-    srcs_lib_whitelist_dirs: ["core/java", "location/java"],
     srcs_lib_whitelist_pkgs: ["android", "com.android.internal.location"],
 }
diff --git a/media/Android.bp b/media/Android.bp
index 29064ad..ef32239 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -67,6 +67,7 @@
         "apex/java/android/media/Session2Link.java",
         "apex/java/android/media/Session2Token.java",
     ],
+    path: "apex/java",
 }
 
 filegroup {
@@ -79,6 +80,7 @@
         "apex/java/android/media/IMediaSession2.aidl",
         "apex/java/android/media/IMediaSession2Service.aidl",
     ],
+    path: "apex/java",
 }
 
 filegroup {
@@ -98,6 +100,7 @@
         "apex/java/android/media/BufferingParams.java",
         "apex/java/android/media/ProxyDataSourceCallback.java",
     ],
+    path: "apex/java",
 }
 
 metalava_updatable_media_args = " --error UnhiddenSystemApi " +
diff --git a/media/java/android/media/IMediaRoute2ProviderClient.aidl b/media/java/android/media/IMediaRoute2ProviderClient.aidl
index 8d08beb..6f44d45 100644
--- a/media/java/android/media/IMediaRoute2ProviderClient.aidl
+++ b/media/java/android/media/IMediaRoute2ProviderClient.aidl
@@ -22,5 +22,5 @@
  * @hide
  */
 oneway interface IMediaRoute2ProviderClient {
-    void notifyProviderInfoUpdated(in MediaRoute2ProviderInfo info);
+    void updateProviderInfo(in MediaRoute2ProviderInfo info);
 }
diff --git a/media/java/android/media/IMediaRouter2Client.aidl b/media/java/android/media/IMediaRouter2Client.aidl
index 774d6a7..26184af 100644
--- a/media/java/android/media/IMediaRouter2Client.aidl
+++ b/media/java/android/media/IMediaRouter2Client.aidl
@@ -16,10 +16,12 @@
 
 package android.media;
 
+import android.media.MediaRoute2ProviderInfo;
+
 /**
  * @hide
  */
 oneway interface IMediaRouter2Client {
-    void notifyStateChanged();
     void notifyRestoreRoute();
+    void notifyProviderInfosUpdated(in List<MediaRoute2ProviderInfo> providers);
 }
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 08266a5..1b713b6 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -42,7 +42,7 @@
     void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction);
 
     // Methods for media router 2
-    void registerClient2AsUser(IMediaRouter2Client client, String packageName, int userId);
+    void registerClient2(IMediaRouter2Client client, String packageName);
     void unregisterClient2(IMediaRouter2Client client);
     void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request);
     /**
@@ -54,8 +54,7 @@
     void selectRoute2(IMediaRouter2Client client, in @nullable MediaRoute2Info route);
     void setControlCategories(IMediaRouter2Client client, in List<String> categories);
 
-    void registerManagerAsUser(IMediaRouter2Manager manager,
-            String packageName, int userId);
+    void registerManager(IMediaRouter2Manager manager, String packageName);
     void unregisterManager(IMediaRouter2Manager manager);
     /**
      * Changes the selected route of an application.
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index b89b0b1..e8e0f82 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -98,7 +98,7 @@
             return;
         }
         try {
-            mClient.notifyProviderInfoUpdated(mProviderInfo);
+            mClient.updateProviderInfo(mProviderInfo);
         } catch (RemoteException ex) {
             Log.w(TAG, "Failed to send onProviderInfoUpdated");
         }
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index f4dffa2..8e29e34 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -16,13 +16,16 @@
 
 package android.media;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -58,6 +61,12 @@
     private Client mClient;
 
     private final String mPackageName;
+    final Handler mHandler;
+
+    List<MediaRoute2ProviderInfo> mProviders = Collections.emptyList();
+    volatile List<MediaRoute2Info> mRoutes = Collections.emptyList();
+
+    MediaRoute2Info mSelectedRoute;
 
     /**
      * Gets an instance of the media router associated with the context.
@@ -78,6 +87,7 @@
                 ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
         mPackageName = mContext.getPackageName();
         //TODO: read control categories from the manifest
+        mHandler = new Handler(Looper.getMainLooper());
     }
 
     /**
@@ -100,25 +110,10 @@
         Objects.requireNonNull(executor, "executor must not be null");
         Objects.requireNonNull(callback, "callback must not be null");
 
+        CallbackRecord record;
         // This is required to prevent adding the same callback twice.
         synchronized (mCallbackRecords) {
-            if (mCallbackRecords.size() == 0) {
-                synchronized (sLock) {
-                    Client client = new Client();
-                    try {
-                        mMediaRouterService.registerClient2AsUser(client, mPackageName,
-                                UserHandle.myUserId());
-                        //TODO: We should merge control categories of callbacks.
-                        mMediaRouterService.setControlCategories(client, mControlCategories);
-                        mClient = client;
-                    } catch (RemoteException ex) {
-                        Log.e(TAG, "Unable to register media router.", ex);
-                    }
-                }
-            }
-
             final int index = findCallbackRecordIndexLocked(callback);
-            CallbackRecord record;
             if (index < 0) {
                 record = new CallbackRecord(callback);
                 mCallbackRecords.add(record);
@@ -129,6 +124,20 @@
             record.mFlags = flags;
         }
 
+        synchronized (sLock) {
+            if (mClient == null) {
+                Client client = new Client();
+                try {
+                    mMediaRouterService.registerClient2(client, mPackageName);
+                    mMediaRouterService.setControlCategories(client, mControlCategories);
+                    mClient = client;
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "Unable to register media router.", ex);
+                }
+            }
+        }
+        record.notifyRoutes();
+
         //TODO: Update discovery request here.
     }
 
@@ -172,10 +181,9 @@
         Objects.requireNonNull(controlCategories, "control categories must not be null");
 
         Client client;
-        List<String> newControlCategories;
+        List<String> newControlCategories = new ArrayList<>(controlCategories);
         synchronized (sLock) {
-            mControlCategories = new ArrayList<>(controlCategories);
-            newControlCategories = mControlCategories;
+            mControlCategories = newControlCategories;
             client = mClient;
         }
         if (client != null) {
@@ -185,8 +193,29 @@
                 Log.e(TAG, "Unable to set control categories.", ex);
             }
         }
+        mHandler.sendMessage(obtainMessage(MediaRouter2::refreshAndNotifyRoutes, this));
     }
 
+    /**
+     * Gets the list of {@link MediaRoute2Info routes} currently known to the media router.
+     *
+     * @return the list of routes that support at least one of the control categories set by
+     * the application
+     */
+    @NonNull
+    public List<MediaRoute2Info> getRoutes() {
+        return mRoutes;
+    }
+
+    /**
+     * Gets the currently selected route.
+     *
+     * @return the selected route
+     */
+    @NonNull
+    public MediaRoute2Info getSelectedRoute() {
+        return mSelectedRoute;
+    }
 
     /**
      * Selects the specified route.
@@ -199,6 +228,7 @@
 
         Client client;
         synchronized (sLock) {
+            mSelectedRoute = route;
             client = mClient;
         }
         if (client != null) {
@@ -247,6 +277,61 @@
         return -1;
     }
 
+    void onProviderInfosUpdated(List<MediaRoute2ProviderInfo> providers) {
+        if (providers == null) {
+            Log.w(TAG, "Providers info is null.");
+            return;
+        }
+
+        mProviders = providers;
+        refreshAndNotifyRoutes();
+    }
+
+    void refreshAndNotifyRoutes() {
+        ArrayList<MediaRoute2Info> routes = new ArrayList<>();
+
+        List<String> controlCategories;
+        synchronized (sLock) {
+            controlCategories = mControlCategories;
+        }
+
+        for (MediaRoute2ProviderInfo provider : mProviders) {
+            updateProvider(provider, controlCategories, routes);
+        }
+
+        //TODO: Can orders be changed?
+        if (!Objects.equals(mRoutes, routes)) {
+            mRoutes = Collections.unmodifiableList(routes);
+            notifyRouteListChanged(mRoutes);
+        }
+    }
+
+    void updateProvider(MediaRoute2ProviderInfo provider, List<String> controlCategories,
+            List<MediaRoute2Info> outRoutes) {
+        if (provider == null || !provider.isValid()) {
+            Log.w(TAG, "Ignoring invalid provider : " + provider);
+        }
+
+        final Collection<MediaRoute2Info> routes = provider.getRoutes();
+        for (MediaRoute2Info route : routes) {
+            if (!route.isValid()) {
+                Log.w(TAG, "Ignoring invalid route : " + route);
+                continue;
+            }
+            if (!route.supportsControlCategory(controlCategories)) {
+                continue;
+            }
+            outRoutes.add(route);
+        }
+    }
+
+    void notifyRouteListChanged(List<MediaRoute2Info> routes) {
+        for (CallbackRecord record: mCallbackRecords) {
+            record.mExecutor.execute(
+                    () -> record.mCallback.onRoutesChanged(routes));
+        }
+    }
+
     /**
      * Interface for receiving events about media routing changes.
      */
@@ -265,9 +350,14 @@
          * Called when a route is removed.
          */
         public void onRouteRemoved(MediaRoute2Info routeInfo) {}
+
+        /**
+         * Called when the list of routes is changed.
+         */
+        public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
     }
 
-    static final class CallbackRecord {
+    final class CallbackRecord {
         public final Callback mCallback;
         public Executor mExecutor;
         public int mFlags;
@@ -275,13 +365,25 @@
         CallbackRecord(@NonNull Callback callback) {
             mCallback = callback;
         }
+
+        void notifyRoutes() {
+            final List<MediaRoute2Info> routes = mRoutes;
+            // notify only when bound to media router service.
+            //TODO: Correct the condition when control category, default rotue, .. are finalized.
+            if (routes.size() > 0) {
+                mExecutor.execute(() -> mCallback.onRoutesChanged(routes));
+            }
+        }
     }
 
     class Client extends IMediaRouter2Client.Stub {
         @Override
-        public void notifyStateChanged() throws RemoteException {}
+        public void notifyRestoreRoute() throws RemoteException {}
 
         @Override
-        public void notifyRestoreRoute() throws RemoteException {}
+        public void notifyProviderInfosUpdated(List<MediaRoute2ProviderInfo> info) {
+            mHandler.sendMessage(obtainMessage(MediaRouter2::onProviderInfosUpdated,
+                    MediaRouter2.this, info));
+        }
     }
 }
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 6c53f7d..0b64569 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -25,7 +25,6 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -104,26 +103,28 @@
         Objects.requireNonNull(executor, "executor must not be null");
         Objects.requireNonNull(callback, "callback must not be null");
 
+        CallbackRecord callbackRecord;
         synchronized (mCallbacks) {
             if (findCallbackRecordIndexLocked(callback) >= 0) {
                 Log.w(TAG, "Ignoring to add the same callback twice.");
                 return;
             }
-            synchronized (sLock) {
-                if (mCallbacks.size() == 0) {
-                    Client client = new Client();
-                    try {
-                        mMediaRouterService.registerManagerAsUser(client, mPackageName,
-                                UserHandle.myUserId());
-                        mClient = client;
-                    } catch (RemoteException ex) {
-                        Log.e(TAG, "Unable to register media router manager.", ex);
-                    }
+            callbackRecord = new CallbackRecord(executor, callback);
+            mCallbacks.add(callbackRecord);
+        }
+
+        synchronized (sLock) {
+            if (mClient == null) {
+                Client client = new Client();
+                try {
+                    mMediaRouterService.registerManager(client, mPackageName);
+                    mClient = client;
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "Unable to register media router manager.", ex);
                 }
+            } else {
+                callbackRecord.notifyRoutes();
             }
-            CallbackRecord record = new CallbackRecord(executor, callback);
-            mCallbacks.add(record);
-            record.notifyRoutes();
         }
     }
 
@@ -149,6 +150,7 @@
                     } catch (RemoteException ex) {
                         Log.e(TAG, "Unable to unregister media router manager", ex);
                     }
+                    mClient.notifyProviderInfosUpdated(Collections.emptyList());
                     mClient = null;
                 }
             }
@@ -255,6 +257,10 @@
             final MediaRoute2ProviderInfo prevProvider = mProviders.get(index);
             final Set<String> updatedRouteIds = new HashSet<>();
             for (MediaRoute2Info routeInfo : routes) {
+                if (!routeInfo.isValid()) {
+                    Log.w(TAG, "Ignoring invalid route : " + routeInfo);
+                    continue;
+                }
                 final MediaRoute2Info prevRoute = prevProvider.getRoute(routeInfo.getId());
                 if (prevRoute == null) {
                     notifyRouteAdded(routeInfo);
@@ -303,7 +309,7 @@
     void notifyRouteListChanged() {
         for (CallbackRecord record: mCallbacks) {
             record.mExecutor.execute(
-                    () -> record.mCallback.onRouteListChanged(mRoutes));
+                    () -> record.mCallback.onRoutesChanged(mRoutes));
         }
     }
 
@@ -369,10 +375,10 @@
         public void onRouteSelected(@NonNull String packageName, @Nullable MediaRoute2Info route) {}
 
         /**
-         * Called when the list of routes are changed.
+         * Called when the list of routes is changed.
          * A client may refresh available routes for each application.
          */
-        public void onRouteListChanged(@NonNull List<MediaRoute2Info> routes) {}
+        public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
     }
 
     final class CallbackRecord {
@@ -385,6 +391,7 @@
         }
 
         void notifyRoutes() {
+            mExecutor.execute(() -> mCallback.onRoutesChanged(mRoutes));
             for (MediaRoute2Info routeInfo : mRoutes) {
                 mExecutor.execute(
                         () -> mCallback.onRouteAdded(routeInfo));
diff --git a/media/lib/signer/Android.bp b/media/lib/signer/Android.bp
index f320397..338ec12 100644
--- a/media/lib/signer/Android.bp
+++ b/media/lib/signer/Android.bp
@@ -19,6 +19,5 @@
     srcs: ["java/**/*.java"],
     api_packages: ["com.android.mediadrm.signer"],
     srcs_lib: "framework-minus-apex",
-    srcs_lib_whitelist_dirs: ["media/java"],
     srcs_lib_whitelist_pkgs: ["android.media"],
 }
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 946fb5e..3abf0a4 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
@@ -42,10 +43,8 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -96,6 +95,7 @@
         mPackageName = mContext.getPackageName();
     }
 
+    //TODO: Move to a seperate file
     @Test
     public void testMediaRoute2Info() {
         MediaRoute2Info routeInfo1 = new MediaRoute2Info.Builder("id", "name")
@@ -159,7 +159,7 @@
         mRouter.unregisterCallback(mockRouterCallback);
 
         verify(mockCallback, timeout(TIMEOUT_MS))
-                .onRouteListChanged(argThat(routes -> routes.size() > 0));
+                .onRoutesChanged(argThat(routes -> routes.size() > 0));
 
         Map<String, MediaRoute2Info> routes =
                 createRouteMap(mManager.getAvailableRoutes(mPackageName));
@@ -170,40 +170,48 @@
         mManager.unregisterCallback(mockCallback);
     }
 
+    /**
+     * Tests if we get proper routes for application that has special control category.
+     */
     @Test
-    public void onRouteSelectedTest() throws Exception {
-        CountDownLatch latch = new CountDownLatch(1);
+    public void testGetRoutes() throws Exception {
+        MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
 
+        mRouter.setControlCategories(CONTROL_CATEGORIES_SPECIAL);
+        mRouter.registerCallback(mExecutor, mockCallback);
+        verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+                .onRoutesChanged(argThat(routes -> routes.size() > 0));
+        Map<String, MediaRoute2Info> routes = createRouteMap(mRouter.getRoutes());
+        Assert.assertEquals(1, routes.size());
+        Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
+
+        mRouter.unregisterCallback(mockCallback);
+    }
+
+    @Test
+    public void testOnRouteSelected() throws Exception {
         MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class);
-        mRouter.registerCallback(mExecutor, mockRouterCallback);
-
-        MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
-            MediaRoute2Info mSelectedRoute = null;
-
-            @Override
-            public void onRouteAdded(MediaRoute2Info routeInfo) {
-                if (mSelectedRoute == null) {
-                    mSelectedRoute = routeInfo;
-                    mManager.selectRoute(mPackageName, mSelectedRoute);
-                }
-            }
-
-            @Override
-            public void onRouteSelected(String packageName, MediaRoute2Info route) {
-                if (TextUtils.equals(packageName, mPackageName)
-                        && mSelectedRoute != null
-                        && route != null
-                        && TextUtils.equals(route.getId(), mSelectedRoute.getId())) {
-                    latch.countDown();
-                }
-            }
-        };
+        MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
 
         mManager.registerCallback(mExecutor, managerCallback);
+        mRouter.setControlCategories(CONTROL_CATEGORIES_ALL);
+        mRouter.registerCallback(mExecutor, mockRouterCallback);
 
-        Assert.assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        verify(managerCallback, timeout(TIMEOUT_MS))
+                .onRoutesChanged(argThat(routes -> routes.size() > 0));
+
+        Map<String, MediaRoute2Info> routes =
+                createRouteMap(mManager.getAvailableRoutes(mPackageName));
+
+        MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
+        mManager.selectRoute(mPackageName, routeToSelect);
+
+        assertNotNull(routeToSelect);
+        verify(managerCallback, timeout(TIMEOUT_MS))
+                .onRouteAdded(argThat(route -> route.equals(routeToSelect)));
 
         mManager.unregisterCallback(managerCallback);
+        mRouter.unregisterCallback(mockRouterCallback);
     }
 
     /**
@@ -219,7 +227,7 @@
         mRouter.registerCallback(mExecutor, routerCallback);
 
         verify(managerCallback, timeout(TIMEOUT_MS))
-                .onRouteListChanged(argThat(routes -> routes.size() > 0));
+                .onRoutesChanged(argThat(routes -> routes.size() > 0));
 
         Map<String, MediaRoute2Info> routes =
                 createRouteMap(mManager.getAvailableRoutes(mPackageName));
@@ -244,7 +252,8 @@
         mManager.unregisterCallback(managerCallback);
     }
 
-    Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
+    // Helper for getting routes easily
+    static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
         Map<String, MediaRoute2Info> routeMap = new HashMap<>();
         for (MediaRoute2Info route : routes) {
             routeMap.put(route.getId(), route);
diff --git a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
index 7846be1..363d885 100644
--- a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="search_menu" msgid="1604061903696928905">"Definições de pesquisa"</string>
+    <string name="search_menu" msgid="1604061903696928905">"Pesquisar definições"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 13890e0..f1fc9f9 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -452,7 +452,7 @@
     <string name="cancel" msgid="6859253417269739139">"বাতিল"</string>
     <string name="okay" msgid="1997666393121016642">"ঠিক আছে"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"চালু করুন"</string>
-    <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"\'বিরক্ত করবেন না\' মোড চালু করুন"</string>
+    <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"\'বিরক্ত করবে না\' মোড চালু করুন"</string>
     <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"কখনও নয়"</string>
     <string name="zen_interruption_level_priority" msgid="2078370238113347720">"শুধুমাত্র অগ্রাধিকার"</string>
     <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 2734f4e..7874aeb 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -286,7 +286,7 @@
     <string name="wait_for_debugger_summary" msgid="1766918303462746804">"Esperar que se conecte el depurador para iniciar la aplicación"</string>
     <string name="debug_input_category" msgid="1811069939601180246">"Entrada"</string>
     <string name="debug_drawing_category" msgid="6755716469267367852">"Dibujo"</string>
-    <string name="debug_hw_drawing_category" msgid="6220174216912308658">"Representación acelerada mediante hardware"</string>
+    <string name="debug_hw_drawing_category" msgid="6220174216912308658">"Procesamiento acelerado mediante hardware"</string>
     <string name="media_category" msgid="4388305075496848353">"Multimedia"</string>
     <string name="debug_monitoring_category" msgid="7640508148375798343">"Supervisión"</string>
     <string name="strict_mode" msgid="1938795874357830695">"Modo estricto"</string>
diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index 5cffafe..7368f1d 100644
--- a/packages/SettingsLib/res/values-hy/arrays.xml
+++ b/packages/SettingsLib/res/values-hy/arrays.xml
@@ -43,7 +43,7 @@
     <item msgid="8937994881315223448">"Միացված է <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ին"</item>
     <item msgid="1330262655415760617">"Անջատված"</item>
     <item msgid="7698638434317271902">"Անջատվում է <xliff:g id="NETWORK_NAME">%1$s</xliff:g>-ից…"</item>
-    <item msgid="197508606402264311">"Անջատած է"</item>
+    <item msgid="197508606402264311">"Անջատված է"</item>
     <item msgid="8578370891960825148">"Անհաջող"</item>
     <item msgid="5660739516542454527">"Արգելափակված"</item>
     <item msgid="1805837518286731242">"Վատ ցանցից ժամանակավոր խուսափում"</item>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index bf58740..a74b4ae 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -263,7 +263,7 @@
     <string name="debug_view_attributes" msgid="6485448367803310384">"Միացնել ցուցադրման հատկանիշների ստուգումը"</string>
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Միշտ ակտիվացրած պահել բջջային տվյալները, նույնիսկ Wi‑Fi-ը միացրած ժամանակ (ցանցերի միջև արագ փոխարկման համար):"</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Օգտագործել սարքակազմի արագացման միացումը, եթե հասանելի է"</string>
-    <string name="adb_warning_title" msgid="6234463310896563253">"Թույլատրե՞լ USB-ի վրիպազերծումը:"</string>
+    <string name="adb_warning_title" msgid="6234463310896563253">"Թույլատրե՞լ USB վրիպազերծումը:"</string>
     <string name="adb_warning_message" msgid="7316799925425402244">"USB վրիպազերծումը միայն ծրագրավորման նպատակների համար է: Օգտագործեք այն ձեր համակարգչից տվյալները ձեր սարք պատճենելու համար, առանց ծանուցման ձեր սարքի վրա ծրագրեր տեղադրելու և տվյալների մատյանը ընթերցելու համար:"</string>
     <string name="adb_keys_warning_message" msgid="5659849457135841625">"Փակե՞լ USB-ի վրիպազերծման մուտքը` անջատելով այն բոլոր համակարգիչներից, որտեղ նախկինում թույլատրել էիք:"</string>
     <string name="dev_settings_warning_title" msgid="7244607768088540165">"Ընդունե՞լ ծրագրավորման կարգավորումներ:"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index cf442b7..57e690d 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -159,7 +159,7 @@
     <string name="tts_default_pitch_title" msgid="6135942113172488671">"ஒலித்திறன்"</string>
     <string name="tts_default_pitch_summary" msgid="1944885882882650009">"உருவாக்கப்படும் பேச்சின் டோன் பாதிக்கப்படும்"</string>
     <string name="tts_default_lang_title" msgid="8018087612299820556">"மொழி"</string>
-    <string name="tts_lang_use_system" msgid="2679252467416513208">"அமைப்பின் மொழியில்"</string>
+    <string name="tts_lang_use_system" msgid="2679252467416513208">"அமைப்பின் மொழியைப் பயன்படுத்தவும்"</string>
     <string name="tts_lang_not_selected" msgid="7395787019276734765">"மொழி தேர்ந்தெடுக்கப்படவில்லை"</string>
     <string name="tts_default_lang_summary" msgid="5219362163902707785">"பேசப்படும் உரைக்கு மொழி சார்ந்த குரலை அமைக்கிறது"</string>
     <string name="tts_play_example_title" msgid="7094780383253097230">"எடுத்துக்காட்டைக் கவனிக்கவும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 83636dc..eb6160a 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -143,7 +143,7 @@
     <string name="data_usage_ota" msgid="5377889154805560860">"సిస్టమ్ అప్‌డేట్‌లు"</string>
     <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB టీథరింగ్"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"పోర్టబుల్ హాట్‌స్పాట్"</string>
-    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"బ్లూటూత్ టీథరింగ్"</string>
+    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"బ్లూటూత్ టెథెరింగ్"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"టీథరింగ్"</string>
     <string name="tether_settings_title_all" msgid="8356136101061143841">"టీథరింగ్ &amp; పోర్టబుల్ హాట్‌స్పాట్"</string>
     <string name="managed_user_title" msgid="8109605045406748842">"అన్ని కార్యాలయ అనువర్తనాలు"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 4e2d6fa..5c06c80 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -201,7 +201,7 @@
     <string name="vpn_settings_not_available" msgid="956841430176985598">"Cài đặt VPN không khả dụng cho người dùng này"</string>
     <string name="tethering_settings_not_available" msgid="6765770438438291012">"Cài đặt chia sẻ kết nối không khả dụng cho người dùng này"</string>
     <string name="apn_settings_not_available" msgid="7873729032165324000">"Cài đặt tên điểm truy cập không khả dụng cho người dùng này"</string>
-    <string name="enable_adb" msgid="7982306934419797485">"Gỡ lỗi USB"</string>
+    <string name="enable_adb" msgid="7982306934419797485">"Gỡ lỗi qua USB"</string>
     <string name="enable_adb_summary" msgid="4881186971746056635">"Bật chế độ gỡ lỗi khi kết nối USB"</string>
     <string name="clear_adb_keys" msgid="4038889221503122743">"Thu hồi ủy quyền gỡ lỗi USB"</string>
     <string name="bugreport_in_power" msgid="7923901846375587241">"Phím tắt báo cáo lỗi"</string>
@@ -263,7 +263,7 @@
     <string name="debug_view_attributes" msgid="6485448367803310384">"Cho phép kiểm tra thuộc tính của chế độ xem"</string>
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Luôn bật dữ liệu di động ngay cả khi Wi-Fi đang hoạt động (để chuyển đổi mạng nhanh)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Sử dụng tính năng tăng tốc phần cứng khi chia sẻ kết nối nếu có"</string>
-    <string name="adb_warning_title" msgid="6234463310896563253">"Cho phép gỡ lỗi USB?"</string>
+    <string name="adb_warning_title" msgid="6234463310896563253">"Cho phép gỡ lỗi qua USB?"</string>
     <string name="adb_warning_message" msgid="7316799925425402244">"Gỡ lỗi USB chỉ dành cho mục đích phát triển. Hãy sử dụng tính năng này để sao chép dữ liệu giữa máy tính và thiết bị của bạn, cài đặt ứng dụng trên thiết bị của bạn mà không thông báo và đọc dữ liệu nhật ký."</string>
     <string name="adb_keys_warning_message" msgid="5659849457135841625">"Thu hồi quyền truy cập gỡ lỗi USB từ tất cả máy tính mà bạn đã ủy quyền trước đó?"</string>
     <string name="dev_settings_warning_title" msgid="7244607768088540165">"Cho phép cài đặt phát triển?"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index e85199f..c641302 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -329,7 +329,7 @@
     <string name="show_all_anrs" msgid="4924885492787069007">"顯示背景 ANR"</string>
     <string name="show_all_anrs_summary" msgid="6636514318275139826">"為背景應用程式顯示「應用程式無回應」對話方塊"</string>
     <string name="show_notification_channel_warnings" msgid="1399948193466922683">"顯示通知管道警告"</string>
-    <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"當應用程式未經有效管道發佈通知時,在畫面上顯示警告"</string>
+    <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"當應用程式未經有效管道發布通知時,在畫面上顯示警告"</string>
     <string name="force_allow_on_external" msgid="3215759785081916381">"強制允許將應用程式寫入外部儲存空間"</string>
     <string name="force_allow_on_external_summary" msgid="3640752408258034689">"允許將任何應用程式寫入外部儲存空間 (無論資訊清單值為何)"</string>
     <string name="force_resizable_activities" msgid="8615764378147824985">"將活動強制設為可調整大小"</string>
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c05609e..b2ff4b3 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -238,6 +238,7 @@
 
         <activity
             android:name=".BugreportWarningActivity"
+            android:theme="@android:style/Theme.DeviceDefault.Dialog.Alert"
             android:finishOnCloseSystemDialogs="true"
             android:excludeFromRecents="true"
             android:exported="false" />
diff --git a/packages/SoundPicker/Android.bp b/packages/SoundPicker/Android.bp
new file mode 100644
index 0000000..3be7ca9
--- /dev/null
+++ b/packages/SoundPicker/Android.bp
@@ -0,0 +1,18 @@
+android_app {
+    name: "SoundPicker",
+    manifest: "AndroidManifest.xml",
+
+    static_libs: [
+        "androidx.appcompat_appcompat",
+    ],
+    resource_dirs: [
+        "res",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    platform_apis: true,
+    certificate: "media",
+    privileged: true,
+}
diff --git a/packages/SoundPicker/AndroidManifest.xml b/packages/SoundPicker/AndroidManifest.xml
new file mode 100644
index 0000000..9d08182
--- /dev/null
+++ b/packages/SoundPicker/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.soundpicker"
+        android:sharedUserId="android.media">
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+
+    <application
+            android:allowBackup="false"
+            android:supportsRtl="true">
+        <receiver android:name="RingtoneReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY"/>
+            </intent-filter>
+        </receiver>
+
+        <service android:name="RingtoneOverlayService" />
+
+        <activity android:name="RingtonePickerActivity"
+                android:theme="@style/PickerDialogTheme"
+                android:enabled="@*android:bool/config_defaultRingtonePickerEnabled"
+                android:excludeFromRecents="true">
+            <intent-filter>
+                <action android:name="android.intent.action.RINGTONE_PICKER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/packages/SoundPicker/res/drawable/ic_add.xml b/packages/SoundPicker/res/drawable/ic_add.xml
new file mode 100644
index 0000000..22b3fe9
--- /dev/null
+++ b/packages/SoundPicker/res/drawable/ic_add.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="?android:attr/colorAccent"
+        android:pathData="M38.0,26.0L26.0,26.0l0.0,12.0l-4.0,0.0L22.0,26.0L10.0,26.0l0.0,-4.0l12.0,0.0L22.0,10.0l4.0,0.0l0.0,12.0l12.0,0.0l0.0,4.0z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SoundPicker/res/drawable/ic_add_padded.xml b/packages/SoundPicker/res/drawable/ic_add_padded.xml
new file mode 100644
index 0000000..c376867
--- /dev/null
+++ b/packages/SoundPicker/res/drawable/ic_add_padded.xml
@@ -0,0 +1,22 @@
+<!--
+    Copyright (C) 2017 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+        android:drawable="@drawable/ic_add"
+        android:insetTop="4dp"
+        android:insetRight="4dp"
+        android:insetBottom="4dp"
+        android:insetLeft="4dp"/>
diff --git a/packages/SoundPicker/res/layout-watch/add_new_sound_item.xml b/packages/SoundPicker/res/layout-watch/add_new_sound_item.xml
new file mode 100644
index 0000000..6f91d77
--- /dev/null
+++ b/packages/SoundPicker/res/layout-watch/add_new_sound_item.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!--
+     Currently, no file manager app on watch could handle ACTION_GET_CONTENT intent.
+     Make the visibility to "gone" to prevent failures.
+ -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeightSmall"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:text="@null"
+        android:textColor="?android:attr/colorAccent"
+        android:gravity="center_vertical"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+        android:drawableStart="@drawable/ic_add_padded"
+        android:drawablePadding="8dp"
+        android:ellipsize="marquee"
+        android:visibility="gone" />
diff --git a/packages/SoundPicker/res/layout-watch/radio_with_work_badge.xml b/packages/SoundPicker/res/layout-watch/radio_with_work_badge.xml
new file mode 100644
index 0000000..ee29a37
--- /dev/null
+++ b/packages/SoundPicker/res/layout-watch/radio_with_work_badge.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.soundpicker.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:background="?android:attr/selectableItemBackground"
+    >
+
+    <CheckedTextView
+        android:id="@+id/checked_text_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeightSmall"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorAlertDialogListItem"
+        android:gravity="center_vertical"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+        android:drawableStart="?android:attr/listChoiceIndicatorSingle"
+        android:drawablePadding="8dp"
+        android:ellipsize="marquee"
+        android:layout_toLeftOf="@+id/work_icon"
+        android:maxLines="3" />
+
+    <ImageView
+        android:id="@id/work_icon"
+        android:layout_width="18dp"
+        android:layout_height="18dp"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:scaleType="centerCrop"
+        android:layout_marginRight="20dp" />
+</com.android.soundpicker.CheckedListItem>
diff --git a/packages/SoundPicker/res/layout/add_new_sound_item.xml b/packages/SoundPicker/res/layout/add_new_sound_item.xml
new file mode 100644
index 0000000..14421c9
--- /dev/null
+++ b/packages/SoundPicker/res/layout/add_new_sound_item.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:background="?android:attr/selectableItemBackground">
+
+<ImageView
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:scaleType="centerCrop"
+        android:layout_marginRight="24dp"
+        android:layout_marginLeft="24dp"
+        android:src="@drawable/ic_add" />
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/add_new_sound_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeightSmall"
+        android:text="@null"
+        android:textColor="?android:attr/colorAccent"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:maxLines="3"
+        android:gravity="center_vertical"
+        android:paddingEnd="?android:attr/dialogPreferredPadding"
+        android:drawablePadding="20dp"
+        android:ellipsize="marquee" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SoundPicker/res/layout/radio_with_work_badge.xml b/packages/SoundPicker/res/layout/radio_with_work_badge.xml
new file mode 100644
index 0000000..c8ca231
--- /dev/null
+++ b/packages/SoundPicker/res/layout/radio_with_work_badge.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.soundpicker.CheckedListItem xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:background="?android:attr/selectableItemBackground"
+    >
+
+    <CheckedTextView
+        android:id="@+id/checked_text_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="?android:attr/listPreferredItemHeightSmall"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorAlertDialogListItem"
+        android:gravity="center_vertical"
+        android:paddingStart="20dp"
+        android:paddingEnd="?android:attr/dialogPreferredPadding"
+        android:drawableStart="?android:attr/listChoiceIndicatorSingle"
+        android:drawablePadding="20dp"
+        android:ellipsize="marquee"
+        android:layout_toLeftOf="@+id/work_icon"
+        android:maxLines="3" />
+
+    <ImageView
+        android:id="@id/work_icon"
+        android:layout_width="18dp"
+        android:layout_height="18dp"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:scaleType="centerCrop"
+        android:layout_marginRight="20dp" />
+</com.android.soundpicker.CheckedListItem>
diff --git a/packages/SoundPicker/res/raw/default_alarm_alert.ogg b/packages/SoundPicker/res/raw/default_alarm_alert.ogg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/SoundPicker/res/raw/default_alarm_alert.ogg
diff --git a/packages/SoundPicker/res/raw/default_notification_sound.ogg b/packages/SoundPicker/res/raw/default_notification_sound.ogg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/SoundPicker/res/raw/default_notification_sound.ogg
diff --git a/packages/SoundPicker/res/raw/default_ringtone.ogg b/packages/SoundPicker/res/raw/default_ringtone.ogg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/SoundPicker/res/raw/default_ringtone.ogg
diff --git a/packages/SoundPicker/res/values-watch/config.xml b/packages/SoundPicker/res/values-watch/config.xml
new file mode 100644
index 0000000..0bc24fa
--- /dev/null
+++ b/packages/SoundPicker/res/values-watch/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds.  Do not translate.
+
+     NOTE: The naming convention is "config_camelCaseValue".  -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- True if the ringtone picker should show the ok/cancel buttons. If it is not shown, the
+    ringtone will be automatically selected when the picker is closed. -->
+    <bool name="config_showOkCancelButtons">false</bool>
+</resources>
diff --git a/packages/SoundPicker/res/values/config.xml b/packages/SoundPicker/res/values/config.xml
new file mode 100644
index 0000000..4e237a2
--- /dev/null
+++ b/packages/SoundPicker/res/values/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds.  Do not translate.
+
+     NOTE: The naming convention is "config_camelCaseValue".  -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- True if the ringtone picker should show the ok/cancel buttons. If it is not shown, the
+    ringtone will be automatically selected when the picker is closed. -->
+    <bool name="config_showOkCancelButtons">true</bool>
+</resources>
diff --git a/packages/SoundPicker/res/values/strings.xml b/packages/SoundPicker/res/values/strings.xml
new file mode 100644
index 0000000..56ed5fd
--- /dev/null
+++ b/packages/SoundPicker/res/values/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Choice in the ringtone picker.  If chosen, the default ringtone will be used. -->
+    <string name="ringtone_default">Default ringtone</string>
+
+    <!-- Choice in the notification sound picker.  If chosen, the default notification sound will be
+         used. -->
+    <string name="notification_sound_default">Default notification sound</string>
+
+    <!-- Choice in the alarm sound picker.  If chosen, the default alarm sound will be used. -->
+    <string name="alarm_sound_default">Default alarm sound</string>
+
+    <!-- Text for the RingtonePicker item that allows adding a new ringtone. -->
+    <string name="add_ringtone_text">Add ringtone</string>
+    <!-- Text for the RingtonePicker item that allows adding a new alarm. -->
+    <string name="add_alarm_text">Add alarm</string>
+    <!-- Text for the RingtonePicker item that allows adding a new notification. -->
+    <string name="add_notification_text">Add notification</string>
+    <!-- Text for the RingtonePicker item ContextMenu that allows deleting a custom ringtone. -->
+    <string name="delete_ringtone_text">Delete</string>
+    <!-- Text for the Toast displayed when adding a custom ringtone fails. -->
+    <string name="unable_to_add_ringtone">Unable to add custom ringtone</string>
+    <!-- Text for the Toast displayed when deleting a custom ringtone fails. -->
+    <string name="unable_to_delete_ringtone">Unable to delete custom ringtone</string>
+</resources>
diff --git a/packages/SoundPicker/res/values/styles.xml b/packages/SoundPicker/res/values/styles.xml
new file mode 100644
index 0000000..d22d9c4
--- /dev/null
+++ b/packages/SoundPicker/res/values/styles.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <style name="PickerDialogTheme" parent="@*android:style/Theme.DeviceDefault.Settings.Dialog">
+    </style>
+
+</resources>
diff --git a/packages/SoundPicker/src/com/android/soundpicker/CheckedListItem.java b/packages/SoundPicker/src/com/android/soundpicker/CheckedListItem.java
new file mode 100644
index 0000000..bde87cf
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/CheckedListItem.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package com.android.soundpicker;
+
+import android.content.Context;
+import android.widget.Checkable;
+import android.widget.CheckedTextView;
+import android.widget.RelativeLayout;
+import android.util.AttributeSet;
+
+/**
+ * The {@link CheckedListItem} is a layout item that represents a ringtone, and is used in
+ * {@link RingtonePickerActivity}. It contains the ringtone's name, and a work badge to right of the
+ * name if the ringtone belongs to a work profile.
+ */
+public class CheckedListItem extends RelativeLayout implements Checkable {
+
+    public CheckedListItem(Context context) {
+        super(context);
+    }
+
+    public CheckedListItem(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CheckedListItem(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public CheckedListItem(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public void setChecked(boolean checked) {
+        getCheckedTextView().setChecked(checked);
+    }
+
+    @Override
+    public boolean isChecked() {
+        return getCheckedTextView().isChecked();
+    }
+
+    @Override
+    public void toggle() {
+        getCheckedTextView().toggle();
+    }
+
+    private CheckedTextView getCheckedTextView() {
+        return (CheckedTextView) findViewById(R.id.checked_text_view);
+    }
+
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneOverlayService.java b/packages/SoundPicker/src/com/android/soundpicker/RingtoneOverlayService.java
new file mode 100644
index 0000000..2d37b4c
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtoneOverlayService.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.soundpicker;
+
+import android.app.Service;
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.IBinder;
+import android.provider.MediaStore;
+import android.provider.Settings.System;
+import android.util.Log;
+
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Service to copy and set customization of default sounds
+ */
+public class RingtoneOverlayService extends Service {
+    private static final String TAG = "RingtoneOverlayService";
+    private static final boolean DEBUG = false;
+
+    @Override
+    public int onStartCommand(@Nullable final Intent intent, final int flags, final int startId) {
+        AsyncTask.execute(() -> {
+            updateRingtones();
+            stopSelf();
+        });
+
+        // Try again later if we are killed before we finish.
+        return Service.START_REDELIVER_INTENT;
+    }
+
+    @Override
+    public IBinder onBind(@Nullable final Intent intent) {
+        return null;
+    }
+
+    private void updateRingtones() {
+        copyResourceAndSetAsSound(R.raw.default_ringtone,
+                System.RINGTONE, Environment.DIRECTORY_RINGTONES);
+        copyResourceAndSetAsSound(R.raw.default_notification_sound,
+                System.NOTIFICATION_SOUND, Environment.DIRECTORY_NOTIFICATIONS);
+        copyResourceAndSetAsSound(R.raw.default_alarm_alert,
+                System.ALARM_ALERT, Environment.DIRECTORY_ALARMS);
+    }
+
+    /* If the resource contains any data, copy a resource to the file system, scan it, and set the
+     * file URI as the default for a sound. */
+    private void copyResourceAndSetAsSound(@IdRes final int id, @NonNull final String name,
+            @NonNull final String subPath) {
+        final File destDir = Environment.getExternalStoragePublicDirectory(subPath);
+        if (!destDir.exists() && !destDir.mkdirs()) {
+            Log.e(TAG, "can't create " + destDir.getAbsolutePath());
+            return;
+        }
+
+        final File dest = new File(destDir, "default_" + name + ".ogg");
+        try (
+            InputStream is = getResources().openRawResource(id);
+            FileOutputStream os = new FileOutputStream(dest);
+        ) {
+            if (is.available() > 0) {
+                FileUtils.copy(is, os);
+                final Uri uri = scanFile(dest);
+                if (uri != null) {
+                    set(name, uri);
+                }
+            } else {
+                // TODO Shall we remove any former copied resource in this case and unset
+                // the defaults if we use this event a second time to clear the data?
+                if (DEBUG) Log.d(TAG, "Resource for " + name + " has no overlay");
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to open resource for " + name + ": " + e);
+        }
+    }
+
+    private Uri scanFile(@NonNull final File file) {
+        return MediaStore.scanFile(this, file);
+    }
+
+    private void set(@NonNull final String name, @NonNull final Uri uri) {
+        final Uri settingUri = System.getUriFor(name);
+        RingtoneManager.setActualDefaultRingtoneUri(this,
+                RingtoneManager.getDefaultType(settingUri), uri);
+        System.putInt(getContentResolver(), name + "_set", 1);
+    }
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
new file mode 100644
index 0000000..4ba5146
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -0,0 +1,762 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.soundpicker;
+
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.database.Cursor;
+import android.database.CursorWrapper;
+import android.media.AudioAttributes;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.MediaStore;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.CursorAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * The {@link RingtonePickerActivity} allows the user to choose one from all of the
+ * available ringtones. The chosen ringtone's URI will be persisted as a string.
+ *
+ * @see RingtoneManager#ACTION_RINGTONE_PICKER
+ */
+public final class RingtonePickerActivity extends AlertActivity implements
+        AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener,
+        AlertController.AlertParams.OnPrepareListViewListener {
+
+    private static final int POS_UNKNOWN = -1;
+
+    private static final String TAG = "RingtonePickerActivity";
+
+    private static final int DELAY_MS_SELECTION_PLAYED = 300;
+
+    private static final String COLUMN_LABEL = MediaStore.Audio.Media.TITLE;
+
+    private static final String SAVE_CLICKED_POS = "clicked_pos";
+
+    private static final String SOUND_NAME_RES_PREFIX = "sound_name_";
+
+    private static final int ADD_FILE_REQUEST_CODE = 300;
+
+    private RingtoneManager mRingtoneManager;
+    private int mType;
+
+    private Cursor mCursor;
+    private Handler mHandler;
+    private BadgedRingtoneAdapter mAdapter;
+
+    /** The position in the list of the 'Silent' item. */
+    private int mSilentPos = POS_UNKNOWN;
+
+    /** The position in the list of the 'Default' item. */
+    private int mDefaultRingtonePos = POS_UNKNOWN;
+
+    /** The position in the list of the ringtone to sample. */
+    private int mSampleRingtonePos = POS_UNKNOWN;
+
+    /** Whether this list has the 'Silent' item. */
+    private boolean mHasSilentItem;
+
+    /** The Uri to place a checkmark next to. */
+    private Uri mExistingUri;
+
+    /** The number of static items in the list. */
+    private int mStaticItemCount;
+
+    /** Whether this list has the 'Default' item. */
+    private boolean mHasDefaultItem;
+
+    /** The Uri to play when the 'Default' item is clicked. */
+    private Uri mUriForDefaultItem;
+
+    /** Id of the user to which the ringtone picker should list the ringtones */
+    private int mPickerUserId;
+
+    /** Context of the user specified by mPickerUserId */
+    private Context mTargetContext;
+
+    /**
+     * A Ringtone for the default ringtone. In most cases, the RingtoneManager
+     * will stop the previous ringtone. However, the RingtoneManager doesn't
+     * manage the default ringtone for us, so we should stop this one manually.
+     */
+    private Ringtone mDefaultRingtone;
+
+    /**
+     * The ringtone that's currently playing, unless the currently playing one is the default
+     * ringtone.
+     */
+    private Ringtone mCurrentRingtone;
+
+    /**
+     * Stable ID for the ringtone that is currently checked (may be -1 if no ringtone is checked).
+     */
+    private long mCheckedItemId = -1;
+
+    private int mAttributesFlags;
+
+    private boolean mShowOkCancelButtons;
+
+    /**
+     * Keep the currently playing ringtone around when changing orientation, so that it
+     * can be stopped later, after the activity is recreated.
+     */
+    private static Ringtone sPlayingRingtone;
+
+    private DialogInterface.OnClickListener mRingtoneClickListener =
+            new DialogInterface.OnClickListener() {
+
+        /*
+         * On item clicked
+         */
+        public void onClick(DialogInterface dialog, int which) {
+            if (which == mCursor.getCount() + mStaticItemCount) {
+                // The "Add new ringtone" item was clicked. Start a file picker intent to select
+                // only audio files (MIME type "audio/*")
+                final Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
+                chooseFile.setType("audio/*");
+                chooseFile.putExtra(Intent.EXTRA_MIME_TYPES,
+                        new String[] { "audio/*", "application/ogg" });
+                startActivityForResult(chooseFile, ADD_FILE_REQUEST_CODE);
+                return;
+            }
+
+            // Save the position of most recently clicked item
+            setCheckedItem(which);
+
+            // In the buttonless (watch-only) version, preemptively set our result since we won't
+            // have another chance to do so before the activity closes.
+            if (!mShowOkCancelButtons) {
+                setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+            }
+
+            // Play clip
+            playRingtone(which, 0);
+        }
+
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mHandler = new Handler();
+
+        Intent intent = getIntent();
+        mPickerUserId = UserHandle.myUserId();
+        mTargetContext = this;
+
+        // Get the types of ringtones to show
+        mType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, -1);
+        initRingtoneManager();
+
+        /*
+         * Get whether to show the 'Default' item, and the URI to play when the
+         * default is clicked
+         */
+        mHasDefaultItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+        mUriForDefaultItem = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI);
+        if (mUriForDefaultItem == null) {
+            if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+                mUriForDefaultItem = Settings.System.DEFAULT_NOTIFICATION_URI;
+            } else if (mType == RingtoneManager.TYPE_ALARM) {
+                mUriForDefaultItem = Settings.System.DEFAULT_ALARM_ALERT_URI;
+            } else if (mType == RingtoneManager.TYPE_RINGTONE) {
+                mUriForDefaultItem = Settings.System.DEFAULT_RINGTONE_URI;
+            } else {
+                // or leave it null for silence.
+                mUriForDefaultItem = Settings.System.DEFAULT_RINGTONE_URI;
+            }
+        }
+
+        // Get whether to show the 'Silent' item
+        mHasSilentItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
+        // AudioAttributes flags
+        mAttributesFlags |= intent.getIntExtra(
+                RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS,
+                0 /*defaultValue == no flags*/);
+
+        mShowOkCancelButtons = getResources().getBoolean(R.bool.config_showOkCancelButtons);
+
+        // The volume keys will control the stream that we are choosing a ringtone for
+        setVolumeControlStream(mRingtoneManager.inferStreamType());
+
+        // Get the URI whose list item should have a checkmark
+        mExistingUri = intent
+                .getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI);
+
+        // Create the list of ringtones and hold on to it so we can update later.
+        mAdapter = new BadgedRingtoneAdapter(this, mCursor,
+                /* isManagedProfile = */ UserManager.get(this).isManagedProfile(mPickerUserId));
+        if (savedInstanceState != null) {
+            setCheckedItem(savedInstanceState.getInt(SAVE_CLICKED_POS, POS_UNKNOWN));
+        }
+
+        final AlertController.AlertParams p = mAlertParams;
+        p.mAdapter = mAdapter;
+        p.mOnClickListener = mRingtoneClickListener;
+        p.mLabelColumn = COLUMN_LABEL;
+        p.mIsSingleChoice = true;
+        p.mOnItemSelectedListener = this;
+        if (mShowOkCancelButtons) {
+            p.mPositiveButtonText = getString(com.android.internal.R.string.ok);
+            p.mPositiveButtonListener = this;
+            p.mNegativeButtonText = getString(com.android.internal.R.string.cancel);
+            p.mPositiveButtonListener = this;
+        }
+        p.mOnPrepareListViewListener = this;
+
+        p.mTitle = intent.getCharSequenceExtra(RingtoneManager.EXTRA_RINGTONE_TITLE);
+        if (p.mTitle == null) {
+          if (mType == RingtoneManager.TYPE_ALARM) {
+              p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title_alarm);
+          } else if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+              p.mTitle =
+                  getString(com.android.internal.R.string.ringtone_picker_title_notification);
+          } else {
+              p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title);
+          }
+        }
+
+        setupAlert();
+    }
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putInt(SAVE_CLICKED_POS, getCheckedItem());
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        if (requestCode == ADD_FILE_REQUEST_CODE && resultCode == RESULT_OK) {
+            // Add the custom ringtone in a separate thread
+            final AsyncTask<Uri, Void, Uri> installTask = new AsyncTask<Uri, Void, Uri>() {
+                @Override
+                protected Uri doInBackground(Uri... params) {
+                    try {
+                        return mRingtoneManager.addCustomExternalRingtone(params[0], mType);
+                    } catch (IOException | IllegalArgumentException e) {
+                        Log.e(TAG, "Unable to add new ringtone", e);
+                    }
+                    return null;
+                }
+
+                @Override
+                protected void onPostExecute(Uri ringtoneUri) {
+                    if (ringtoneUri != null) {
+                        requeryForAdapter();
+                    } else {
+                        // Ringtone was not added, display error Toast
+                        Toast.makeText(RingtonePickerActivity.this, R.string.unable_to_add_ringtone,
+                                Toast.LENGTH_SHORT).show();
+                    }
+                }
+            };
+            installTask.execute(data.getData());
+        }
+    }
+
+    // Disabled because context menus aren't Material Design :(
+    /*
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+        int position = ((AdapterContextMenuInfo) menuInfo).position;
+
+        Ringtone ringtone = getRingtone(getRingtoneManagerPosition(position));
+        if (ringtone != null && mRingtoneManager.isCustomRingtone(ringtone.getUri())) {
+            // It's a custom ringtone so we display the context menu
+            menu.setHeaderTitle(ringtone.getTitle(this));
+            menu.add(Menu.NONE, Menu.FIRST, Menu.NONE, R.string.delete_ringtone_text);
+        }
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case Menu.FIRST: {
+                int deletedRingtonePos = ((AdapterContextMenuInfo) item.getMenuInfo()).position;
+                Uri deletedRingtoneUri = getRingtone(
+                        getRingtoneManagerPosition(deletedRingtonePos)).getUri();
+                if(mRingtoneManager.deleteExternalRingtone(deletedRingtoneUri)) {
+                    requeryForAdapter();
+                } else {
+                    Toast.makeText(this, R.string.unable_to_delete_ringtone, Toast.LENGTH_SHORT)
+                            .show();
+                }
+                return true;
+            }
+            default: {
+                return false;
+            }
+        }
+    }
+    */
+
+    @Override
+    public void onDestroy() {
+        if (mCursor != null) {
+            mCursor.close();
+            mCursor = null;
+        }
+        super.onDestroy();
+    }
+
+    public void onPrepareListView(ListView listView) {
+        // Reset the static item count, as this method can be called multiple times
+        mStaticItemCount = 0;
+
+        if (mHasDefaultItem) {
+            mDefaultRingtonePos = addDefaultRingtoneItem(listView);
+
+            if (getCheckedItem() == POS_UNKNOWN && RingtoneManager.isDefault(mExistingUri)) {
+                setCheckedItem(mDefaultRingtonePos);
+            }
+        }
+
+        if (mHasSilentItem) {
+            mSilentPos = addSilentItem(listView);
+
+            // The 'Silent' item should use a null Uri
+            if (getCheckedItem() == POS_UNKNOWN && mExistingUri == null) {
+                setCheckedItem(mSilentPos);
+            }
+        }
+
+        if (getCheckedItem() == POS_UNKNOWN) {
+            setCheckedItem(getListPosition(mRingtoneManager.getRingtonePosition(mExistingUri)));
+        }
+
+        // In the buttonless (watch-only) version, preemptively set our result since we won't
+        // have another chance to do so before the activity closes.
+        if (!mShowOkCancelButtons) {
+            setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+        }
+        // If external storage is available, add a button to install sounds from storage.
+        if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+            addNewSoundItem(listView);
+        }
+
+        // Enable context menu in ringtone items
+        registerForContextMenu(listView);
+    }
+
+    /**
+     * Re-query RingtoneManager for the most recent set of installed ringtones. May move the
+     * selected item position to match the new position of the chosen sound.
+     *
+     * This should only need to happen after adding or removing a ringtone.
+     */
+    private void requeryForAdapter() {
+        // Refresh and set a new cursor, closing the old one.
+        initRingtoneManager();
+        mAdapter.changeCursor(mCursor);
+
+        // Update checked item location.
+        int checkedPosition = POS_UNKNOWN;
+        for (int i = 0; i < mAdapter.getCount(); i++) {
+            if (mAdapter.getItemId(i) == mCheckedItemId) {
+                checkedPosition = getListPosition(i);
+                break;
+            }
+        }
+        if (mHasSilentItem && checkedPosition == POS_UNKNOWN) {
+            checkedPosition = mSilentPos;
+        }
+        setCheckedItem(checkedPosition);
+        setupAlert();
+    }
+
+    /**
+     * Adds a static item to the top of the list. A static item is one that is not from the
+     * RingtoneManager.
+     *
+     * @param listView The ListView to add to.
+     * @param textResId The resource ID of the text for the item.
+     * @return The position of the inserted item.
+     */
+    private int addStaticItem(ListView listView, int textResId) {
+        TextView textView = (TextView) getLayoutInflater().inflate(
+                com.android.internal.R.layout.select_dialog_singlechoice_material, listView, false);
+        textView.setText(textResId);
+        listView.addHeaderView(textView);
+        mStaticItemCount++;
+        return listView.getHeaderViewsCount() - 1;
+    }
+
+    private int addDefaultRingtoneItem(ListView listView) {
+        if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+            return addStaticItem(listView, R.string.notification_sound_default);
+        } else if (mType == RingtoneManager.TYPE_ALARM) {
+            return addStaticItem(listView, R.string.alarm_sound_default);
+        }
+
+        return addStaticItem(listView, R.string.ringtone_default);
+    }
+
+    private int addSilentItem(ListView listView) {
+        return addStaticItem(listView, com.android.internal.R.string.ringtone_silent);
+    }
+
+    private void addNewSoundItem(ListView listView) {
+        View view = getLayoutInflater().inflate(R.layout.add_new_sound_item, listView,
+                false /* attachToRoot */);
+        TextView text = (TextView)view.findViewById(R.id.add_new_sound_text);
+
+        if (mType == RingtoneManager.TYPE_ALARM) {
+            text.setText(R.string.add_alarm_text);
+        } else if (mType == RingtoneManager.TYPE_NOTIFICATION) {
+            text.setText(R.string.add_notification_text);
+        } else {
+            text.setText(R.string.add_ringtone_text);
+        }
+        listView.addFooterView(view);
+    }
+
+    private void initRingtoneManager() {
+        // Reinstantiate the RingtoneManager. Cursor.requery() was deprecated and calling it
+        // causes unexpected behavior.
+        mRingtoneManager = new RingtoneManager(mTargetContext, /* includeParentRingtones */ true);
+        if (mType != -1) {
+            mRingtoneManager.setType(mType);
+        }
+        mCursor = new LocalizedCursor(mRingtoneManager.getCursor(), getResources(), COLUMN_LABEL);
+    }
+
+    private Ringtone getRingtone(int ringtoneManagerPosition) {
+        if (ringtoneManagerPosition < 0) {
+            return null;
+        }
+        return mRingtoneManager.getRingtone(ringtoneManagerPosition);
+    }
+
+    private int getCheckedItem() {
+        return mAlertParams.mCheckedItem;
+    }
+
+    private void setCheckedItem(int pos) {
+        mAlertParams.mCheckedItem = pos;
+        mCheckedItemId = mAdapter.getItemId(getRingtoneManagerPosition(pos));
+    }
+
+    /*
+     * On click of Ok/Cancel buttons
+     */
+    public void onClick(DialogInterface dialog, int which) {
+        boolean positiveResult = which == DialogInterface.BUTTON_POSITIVE;
+
+        // Stop playing the previous ringtone
+        mRingtoneManager.stopPreviousRingtone();
+
+        if (positiveResult) {
+            setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+        } else {
+            setResult(RESULT_CANCELED);
+        }
+
+        finish();
+    }
+
+    /*
+     * On item selected via keys
+     */
+    public void onItemSelected(AdapterView parent, View view, int position, long id) {
+        // footer view
+        if (position >= mCursor.getCount() + mStaticItemCount) {
+            return;
+        }
+
+        playRingtone(position, DELAY_MS_SELECTION_PLAYED);
+
+        // In the buttonless (watch-only) version, preemptively set our result since we won't
+        // have another chance to do so before the activity closes.
+        if (!mShowOkCancelButtons) {
+            setSuccessResultWithRingtone(getCurrentlySelectedRingtoneUri());
+        }
+    }
+
+    public void onNothingSelected(AdapterView parent) {
+    }
+
+    private void playRingtone(int position, int delayMs) {
+        mHandler.removeCallbacks(this);
+        mSampleRingtonePos = position;
+        mHandler.postDelayed(this, delayMs);
+    }
+
+    public void run() {
+        stopAnyPlayingRingtone();
+        if (mSampleRingtonePos == mSilentPos) {
+            return;
+        }
+
+        Ringtone ringtone;
+        if (mSampleRingtonePos == mDefaultRingtonePos) {
+            if (mDefaultRingtone == null) {
+                mDefaultRingtone = RingtoneManager.getRingtone(this, mUriForDefaultItem);
+            }
+           /*
+            * Stream type of mDefaultRingtone is not set explicitly here.
+            * It should be set in accordance with mRingtoneManager of this Activity.
+            */
+            if (mDefaultRingtone != null) {
+                mDefaultRingtone.setStreamType(mRingtoneManager.inferStreamType());
+            }
+            ringtone = mDefaultRingtone;
+            mCurrentRingtone = null;
+        } else {
+            ringtone = mRingtoneManager.getRingtone(getRingtoneManagerPosition(mSampleRingtonePos));
+            mCurrentRingtone = ringtone;
+        }
+
+        if (ringtone != null) {
+            if (mAttributesFlags != 0) {
+                ringtone.setAudioAttributes(
+                        new AudioAttributes.Builder(ringtone.getAudioAttributes())
+                                .setFlags(mAttributesFlags)
+                                .build());
+            }
+            ringtone.play();
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        if (!isChangingConfigurations()) {
+            stopAnyPlayingRingtone();
+        } else {
+            saveAnyPlayingRingtone();
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (!isChangingConfigurations()) {
+            stopAnyPlayingRingtone();
+        }
+    }
+
+    private void setSuccessResultWithRingtone(Uri ringtoneUri) {
+      setResult(RESULT_OK,
+          new Intent().putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, ringtoneUri));
+    }
+
+    private Uri getCurrentlySelectedRingtoneUri() {
+      if (getCheckedItem() == mDefaultRingtonePos) {
+        // Use the default Uri that they originally gave us.
+        return mUriForDefaultItem;
+      } else if (getCheckedItem() == mSilentPos) {
+        // Use a null Uri for the 'Silent' item.
+        return null;
+      } else {
+        return mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(getCheckedItem()));
+      }
+    }
+
+    private void saveAnyPlayingRingtone() {
+        if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
+            sPlayingRingtone = mDefaultRingtone;
+        } else if (mCurrentRingtone != null && mCurrentRingtone.isPlaying()) {
+            sPlayingRingtone = mCurrentRingtone;
+        }
+    }
+
+    private void stopAnyPlayingRingtone() {
+        if (sPlayingRingtone != null && sPlayingRingtone.isPlaying()) {
+            sPlayingRingtone.stop();
+        }
+        sPlayingRingtone = null;
+
+        if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
+            mDefaultRingtone.stop();
+        }
+
+        if (mRingtoneManager != null) {
+            mRingtoneManager.stopPreviousRingtone();
+        }
+    }
+
+    private int getRingtoneManagerPosition(int listPos) {
+        return listPos - mStaticItemCount;
+    }
+
+    private int getListPosition(int ringtoneManagerPos) {
+
+        // If the manager position is -1 (for not found), return that
+        if (ringtoneManagerPos < 0) return ringtoneManagerPos;
+
+        return ringtoneManagerPos + mStaticItemCount;
+    }
+
+    private static class LocalizedCursor extends CursorWrapper {
+
+        final int mTitleIndex;
+        final Resources mResources;
+        String mNamePrefix;
+        final Pattern mSanitizePattern;
+
+        LocalizedCursor(Cursor cursor, Resources resources, String columnLabel) {
+            super(cursor);
+            mTitleIndex = mCursor.getColumnIndex(columnLabel);
+            mResources = resources;
+            mSanitizePattern = Pattern.compile("[^a-zA-Z0-9]");
+            if (mTitleIndex == -1) {
+                Log.e(TAG, "No index for column " + columnLabel);
+                mNamePrefix = null;
+            } else {
+                try {
+                    // Build the prefix for the name of the resource to look up
+                    // format is: "ResourcePackageName::ResourceTypeName/"
+                    // (the type name is expected to be "string" but let's not hardcode it).
+                    // Here we use an existing resource "notification_sound_default" which is
+                    // always expected to be found.
+                    mNamePrefix = String.format("%s:%s/%s",
+                            mResources.getResourcePackageName(R.string.notification_sound_default),
+                            mResources.getResourceTypeName(R.string.notification_sound_default),
+                            SOUND_NAME_RES_PREFIX);
+                } catch (NotFoundException e) {
+                    mNamePrefix = null;
+                }
+            }
+        }
+
+        /**
+         * Process resource name to generate a valid resource name.
+         * @param input
+         * @return a non-null String
+         */
+        private String sanitize(String input) {
+            if (input == null) {
+                return "";
+            }
+            return mSanitizePattern.matcher(input).replaceAll("_").toLowerCase();
+        }
+
+        @Override
+        public String getString(int columnIndex) {
+            final String defaultName = mCursor.getString(columnIndex);
+            if ((columnIndex != mTitleIndex) || (mNamePrefix == null)) {
+                return defaultName;
+            }
+            TypedValue value = new TypedValue();
+            try {
+                // the name currently in the database is used to derive a name to match
+                // against resource names in this package
+                mResources.getValue(mNamePrefix + sanitize(defaultName), value, false);
+            } catch (NotFoundException e) {
+                // no localized string, use the default string
+                return defaultName;
+            }
+            if ((value != null) && (value.type == TypedValue.TYPE_STRING)) {
+                Log.d(TAG, String.format("Replacing name %s with %s",
+                        defaultName, value.string.toString()));
+                return value.string.toString();
+            } else {
+                Log.e(TAG, "Invalid value when looking up localized name, using " + defaultName);
+                return defaultName;
+            }
+        }
+    }
+
+    private class BadgedRingtoneAdapter extends CursorAdapter {
+        private final boolean mIsManagedProfile;
+
+        public BadgedRingtoneAdapter(Context context, Cursor cursor, boolean isManagedProfile) {
+            super(context, cursor);
+            mIsManagedProfile = isManagedProfile;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            if (position < 0) {
+                return position;
+            }
+            return super.getItemId(position);
+        }
+
+        @Override
+        public View newView(Context context, Cursor cursor, ViewGroup parent) {
+            LayoutInflater inflater = LayoutInflater.from(context);
+            return inflater.inflate(R.layout.radio_with_work_badge, parent, false);
+        }
+
+        @Override
+        public void bindView(View view, Context context, Cursor cursor) {
+            // Set text as the title of the ringtone
+            ((TextView) view.findViewById(R.id.checked_text_view))
+                    .setText(cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX));
+
+            boolean isWorkRingtone = false;
+            if (mIsManagedProfile) {
+                /*
+                 * Display the work icon if the ringtone belongs to a work profile. We can tell that
+                 * a ringtone belongs to a work profile if the picker user is a managed profile, the
+                 * ringtone Uri is in external storage, and either the uri has no user id or has the
+                 * id of the picker user
+                 */
+                Uri currentUri = mRingtoneManager.getRingtoneUri(cursor.getPosition());
+                int uriUserId = ContentProvider.getUserIdFromUri(currentUri, mPickerUserId);
+                Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(currentUri);
+
+                if (uriUserId == mPickerUserId && uriWithoutUserId.toString()
+                        .startsWith(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString())) {
+                    isWorkRingtone = true;
+                }
+            }
+
+            ImageView workIcon = (ImageView) view.findViewById(R.id.work_icon);
+            if(isWorkRingtone) {
+                workIcon.setImageDrawable(getPackageManager().getUserBadgeForDensityNoBackground(
+                        UserHandle.of(mPickerUserId), -1 /* density */));
+                workIcon.setVisibility(View.VISIBLE);
+            } else {
+                workIcon.setVisibility(View.GONE);
+            }
+        }
+    }
+}
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtoneReceiver.java b/packages/SoundPicker/src/com/android/soundpicker/RingtoneReceiver.java
new file mode 100644
index 0000000..9e1ba3a
--- /dev/null
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtoneReceiver.java
@@ -0,0 +1,37 @@
+/* //device/content/providers/media/src/com/android/providers/media/MediaScannerReceiver.java
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.soundpicker;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class RingtoneReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if (Intent.ACTION_DEVICE_CUSTOMIZATION_READY.equals(action)) {
+            initResourceRingtones(context);
+        }
+    }
+
+    private void initResourceRingtones(Context context) {
+        context.startService(
+                new Intent(context, RingtoneOverlayService.class));
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java
new file mode 100644
index 0000000..cac673f
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/HomeControlsPlugin.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.plugins;
+
+import android.view.ViewGroup;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Test plugin for home controls
+ */
+@ProvidesInterface(action = HomeControlsPlugin.ACTION, version = HomeControlsPlugin.VERSION)
+public interface HomeControlsPlugin extends Plugin {
+
+    String ACTION = "com.android.systemui.action.PLUGIN_HOME_CONTROLS";
+    int VERSION = 1;
+
+    /**
+      * Pass the container for the plugin to use however it wants. Ideally the plugin impl
+      * will add home controls to this space.
+      */
+    void sendParentGroup(ViewGroup group);
+}
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 7d403b2..7a94008 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -55,6 +55,19 @@
             android:clipChildren="false"
             systemui:viewType="com.android.systemui.plugins.qs.QS" />
 
+        <!-- Temporary area to test out home controls -->
+        <LinearLayout
+            android:id="@+id/home_controls_layout"
+            android:layout_width="match_parent"
+            android:layout_height="125dp"
+            android:layout_gravity="@integer/notification_panel_layout_gravity"
+            android:visibility="gone"
+            android:padding="8dp"
+            android:layout_margin="5dp"
+            android:background="?android:attr/colorBackgroundFloating"
+            android:orientation="vertical">
+        </LinearLayout>
+
         <com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
             android:id="@+id/notification_stack_scroller"
             android:layout_marginTop="@dimen/notification_panel_margin_top"
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index 10d132a..45126f3 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -19,16 +19,12 @@
 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
 import static android.telephony.PhoneStateListener.LISTEN_NONE;
 
-import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
-
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
-import android.os.SystemProperties;
-import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
@@ -37,19 +33,17 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import androidx.annotation.VisibleForTesting;
-
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
 import com.android.settingslib.WirelessUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
+import androidx.annotation.VisibleForTesting;
+
 /**
  * Controller that generates text including the carrier names and/or the status of all the SIM
  * interfaces in the device. Through a callback, the updates can be retrieved either as a list or
@@ -72,8 +66,6 @@
     private Context mContext;
     private CharSequence mSeparator;
     private WakefulnessLifecycle mWakefulnessLifecycle;
-    @VisibleForTesting
-    protected boolean mDisplayOpportunisticSubscriptionCarrierText;
     private final WakefulnessLifecycle.Observer mWakefulnessObserver =
             new WakefulnessLifecycle.Observer() {
                 @Override
@@ -174,9 +166,6 @@
         mSimSlotsNumber = ((TelephonyManager) context.getSystemService(
                 Context.TELEPHONY_SERVICE)).getPhoneCount();
         mSimErrorState = new boolean[mSimSlotsNumber];
-        updateDisplayOpportunisticSubscriptionCarrierText(SystemProperties.getBoolean(
-                TelephonyProperties.DISPLAY_OPPORTUNISTIC_SUBSCRIPTION_CARRIER_TEXT_PROPERTY_NAME,
-                false));
     }
 
     /**
@@ -253,63 +242,8 @@
         }
     }
 
-    /**
-     * @param subscriptions
-     */
-    private void filterMobileSubscriptionInSameGroup(List<SubscriptionInfo> subscriptions) {
-        if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) {
-            SubscriptionInfo info1 = subscriptions.get(0);
-            SubscriptionInfo info2 = subscriptions.get(1);
-            if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
-                // If both subscriptions are primary, show both.
-                if (!info1.isOpportunistic() && !info2.isOpportunistic()) return;
-
-                // If carrier required, always show signal bar of primary subscription.
-                // Otherwise, show whichever subscription is currently active for Internet.
-                boolean alwaysShowPrimary = CarrierConfigManager.getDefaultConfig()
-                        .getBoolean(CarrierConfigManager
-                        .KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN);
-                if (alwaysShowPrimary) {
-                    subscriptions.remove(info1.isOpportunistic() ? info1 : info2);
-                } else {
-                    subscriptions.remove(info1.getSubscriptionId() == mActiveMobileDataSubscription
-                            ? info2 : info1);
-                }
-
-            }
-        }
-    }
-
-    /**
-     * updates if opportunistic sub carrier text should be displayed or not
-     *
-     */
-    @VisibleForTesting
-    public void updateDisplayOpportunisticSubscriptionCarrierText(boolean isEnable) {
-        mDisplayOpportunisticSubscriptionCarrierText = isEnable;
-    }
-
     protected List<SubscriptionInfo> getSubscriptionInfo() {
-        List<SubscriptionInfo> subs;
-        if (mDisplayOpportunisticSubscriptionCarrierText) {
-            SubscriptionManager subscriptionManager = ((SubscriptionManager) mContext
-                    .getSystemService(
-                    Context.TELEPHONY_SUBSCRIPTION_SERVICE));
-            subs = subscriptionManager.getActiveSubscriptionInfoList(false);
-            if (subs == null) {
-                subs = new ArrayList<>();
-            } else {
-                filterMobileSubscriptionInSameGroup(subs);
-            }
-        } else {
-            subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
-            if (subs == null) {
-                subs = new ArrayList<>();
-            } else {
-                filterMobileSubscriptionInSameGroup(subs);
-            }
-        }
-        return subs;
+        return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
     }
 
     protected void updateCarrierText() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8c5374a..5d6cc83 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -30,6 +30,7 @@
 import static android.os.BatteryManager.EXTRA_STATUS;
 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
 
+import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -77,6 +78,7 @@
 import android.provider.Settings;
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
@@ -261,6 +263,7 @@
     private boolean mLogoutEnabled;
     // If the user long pressed the lock icon, disabling face auth for the current session.
     private boolean mLockIconPressed;
+    private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
     /**
      * Short delay before restarting biometric authentication after a successful try
@@ -406,9 +409,11 @@
                 }
             };
 
-    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+    @VisibleForTesting
+    public PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
         @Override
         public void onActiveDataSubscriptionIdChanged(int subId) {
+            mActiveMobileDataSubscription = subId;
             mHandler.sendEmptyMessage(MSG_SIM_SUBSCRIPTION_INFO_CHANGED);
         }
     };
@@ -511,7 +516,9 @@
         }
     }
 
-    /** @return List of SubscriptionInfo records, maybe empty but never null */
+    /**
+     * @return List of SubscriptionInfo records, maybe empty but never null.
+     */
     public List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) {
         List<SubscriptionInfo> sil = mSubscriptionInfo;
         if (sil == null || forceReload) {
@@ -523,7 +530,42 @@
         } else {
             mSubscriptionInfo = sil;
         }
-        return mSubscriptionInfo;
+        return new ArrayList<>(mSubscriptionInfo);
+    }
+
+    /**
+     * This method returns filtered list of SubscriptionInfo from {@link #getSubscriptionInfo}.
+     * above. Maybe empty but never null.
+     *
+     * In DSDS mode if both subscriptions are grouped and one is opportunistic, we filter out one
+     * of them based on carrier config. e.g. In this case we should only show one carrier name
+     * on the status bar and quick settings.
+     */
+    public List<SubscriptionInfo> getFilteredSubscriptionInfo(boolean forceReload) {
+        List<SubscriptionInfo> subscriptions = getSubscriptionInfo(false);
+        if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) {
+            SubscriptionInfo info1 = subscriptions.get(0);
+            SubscriptionInfo info2 = subscriptions.get(1);
+            if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) {
+                // If both subscriptions are primary, show both.
+                if (!info1.isOpportunistic() && !info2.isOpportunistic()) return subscriptions;
+
+                // If carrier required, always show signal bar of primary subscription.
+                // Otherwise, show whichever subscription is currently active for Internet.
+                boolean alwaysShowPrimary = CarrierConfigManager.getDefaultConfig()
+                        .getBoolean(CarrierConfigManager
+                        .KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN);
+                if (alwaysShowPrimary) {
+                    subscriptions.remove(info1.isOpportunistic() ? info1 : info2);
+                } else {
+                    subscriptions.remove(info1.getSubscriptionId() == mActiveMobileDataSubscription
+                            ? info2 : info1);
+                }
+
+            }
+        }
+
+        return subscriptions;
     }
 
     @Override
@@ -2700,6 +2742,7 @@
                 pw.println("    " + mSubscriptionInfo.get(i));
             }
         }
+        pw.println("  Current active data subId=" + mActiveMobileDataSubscription);
         pw.println("  Service states:");
         for (int subId : mServiceStates.keySet()) {
             pw.println("    " + subId + "=" + mServiceStates.get(subId));
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 7e3b423..d5c928b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -140,11 +140,12 @@
             LockPatternUtils lockPatternUtils,  ViewGroup container,
             DismissCallbackRegistry dismissCallbackRegistry,
             KeyguardBouncer.BouncerExpansionCallback expansionCallback,
-            FalsingManager falsingManager) {
+            FalsingManager falsingManager, KeyguardBypassController bypassController) {
         return new KeyguardBouncer(context, callback, lockPatternUtils, container,
                 dismissCallbackRegistry, falsingManager,
                 expansionCallback, UnlockMethodCache.getInstance(context),
-                KeyguardUpdateMonitor.getInstance(context), new Handler(Looper.getMainLooper()));
+                KeyguardUpdateMonitor.getInstance(context), bypassController,
+                new Handler(Looper.getMainLooper()));
     }
 
     public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 858ed6d..ef171d3 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -50,7 +50,7 @@
  */
 @Singleton
 public class AppOpsControllerImpl implements AppOpsController,
-        AppOpsManager.OnOpActiveChangedListener,
+        AppOpsManager.OnOpActiveChangedInternalListener,
         AppOpsManager.OnOpNotedListener, Dumpable {
 
     private static final long NOTED_OP_TIME_DELAY_MS = 5000;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index ee79e6b..4120334 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -52,16 +52,17 @@
 
     private FalsingManager mInternalFalsingManager;
     private final Handler mMainHandler;
+    private boolean mBrightlineEnabled;
 
     @Inject
     FalsingManagerProxy(Context context, PluginManager pluginManager,
             @Named(MAIN_HANDLER_NAME) Handler handler) {
         mMainHandler = handler;
+        setupFalsingManager(context);
         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
                 command -> mMainHandler.post(command),
                 properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace())
         );
-        setupFalsingManager(context);
         final PluginListener<FalsingPlugin> mPluginListener = new PluginListener<FalsingPlugin>() {
             public void onPluginConnected(FalsingPlugin plugin, Context context) {
                 FalsingManager pluginFalsingManager = plugin.getFalsingManager(context);
@@ -94,6 +95,10 @@
     public void setupFalsingManager(Context context) {
         boolean brightlineEnabled = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, true);
+        if (brightlineEnabled == mBrightlineEnabled && mInternalFalsingManager != null) {
+            return;
+        }
+        mBrightlineEnabled = brightlineEnabled;
 
         if (mInternalFalsingManager != null) {
             mInternalFalsingManager.cleanup();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index d47288a..8670d1bd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -87,6 +87,7 @@
 
 import libcore.io.IoUtils;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.text.DateFormat;
@@ -254,8 +255,8 @@
             // Save the screenshot to the MediaStore
             final MediaStore.PendingParams params = new MediaStore.PendingParams(
                     MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mImageFileName, "image/png");
-            params.setPrimaryDirectory(Environment.DIRECTORY_PICTURES);
-            params.setSecondaryDirectory(Environment.DIRECTORY_SCREENSHOTS);
+            params.setRelativePath(Environment.DIRECTORY_PICTURES + File.separator
+                    + Environment.DIRECTORY_SCREENSHOTS);
 
             final Uri uri = MediaStore.createPending(context, params);
             final MediaStore.PendingSession session = MediaStore.openPending(context, uri);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
index d1b3c3c..2a5ccdb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
@@ -134,7 +134,7 @@
 
     private void updateText() {
         CharSequence displayText = null;
-        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
+        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
         final int N = subs.size();
         for (int i = 0; i < N; i++) {
             int subId = subs.get(i).getSubscriptionId();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index c4d346c..dc9b373 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -77,6 +77,7 @@
                 }
             };
     private final Runnable mRemoveViewRunnable = this::removeView;
+    private final KeyguardBypassController mKeyguardBypassController;
     protected KeyguardHostView mKeyguardView;
     private final Runnable mResetRunnable = ()-> {
         if (mKeyguardView != null) {
@@ -97,7 +98,8 @@
             LockPatternUtils lockPatternUtils, ViewGroup container,
             DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager,
             BouncerExpansionCallback expansionCallback, UnlockMethodCache unlockMethodCache,
-            KeyguardUpdateMonitor keyguardUpdateMonitor, Handler handler) {
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            KeyguardBypassController keyguardBypassController, Handler handler) {
         mContext = context;
         mCallback = callback;
         mLockPatternUtils = lockPatternUtils;
@@ -109,6 +111,7 @@
         mHandler = handler;
         mUnlockMethodCache = unlockMethodCache;
         mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+        mKeyguardBypassController = keyguardBypassController;
     }
 
     public void show(boolean resetSecuritySelection) {
@@ -171,7 +174,8 @@
         // Split up the work over multiple frames.
         DejankUtils.removeCallbacks(mResetRunnable);
         if (mUnlockMethodCache.isFaceAuthEnabled() && !needsFullscreenBouncer()
-                && !mKeyguardUpdateMonitor.userNeedsStrongAuth()) {
+                && !mKeyguardUpdateMonitor.userNeedsStrongAuth()
+                && !mKeyguardBypassController.getBypassEnabled()) {
             mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
         } else {
             DejankUtils.postAfterTraversal(mShowRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c171730..c76cdcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -53,6 +53,7 @@
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -68,10 +69,13 @@
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.HomeControlsPlugin;
+import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.qs.QSFragment;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.GestureRecorder;
@@ -192,6 +196,7 @@
     private View mQsNavbarScrim;
     protected NotificationsQuickSettingsContainer mNotificationContainerParent;
     protected NotificationStackScrollLayout mNotificationStackScroller;
+    protected LinearLayout mHomeControlsLayout;
     private boolean mAnimateNextPositionUpdate;
 
     private int mTrackingPointer;
@@ -450,6 +455,7 @@
         mBigClockContainer = findViewById(R.id.big_clock_container);
         keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
 
+        mHomeControlsLayout = findViewById(R.id.home_controls_layout);
         mNotificationContainerParent = findViewById(R.id.notification_container_parent);
         mNotificationStackScroller = findViewById(R.id.notification_stack_scroller);
         mNotificationStackScroller.setOnHeightChangedListener(this);
@@ -480,6 +486,21 @@
                 }
             }
         });
+
+        Dependency.get(PluginManager.class).addPluginListener(
+                new PluginListener<HomeControlsPlugin>() {
+
+                    @Override
+                    public void onPluginConnected(HomeControlsPlugin plugin,
+                                                  Context pluginContext) {
+                        plugin.sendParentGroup(mHomeControlsLayout);
+                    }
+
+                    @Override
+                    public void onPluginDisconnected(HomeControlsPlugin plugin) {
+
+                    }
+                }, HomeControlsPlugin.class, false);
     }
 
     @Override
@@ -1270,9 +1291,11 @@
             if (mQsExpandImmediate) {
                 mNotificationStackScroller.setVisibility(View.GONE);
                 mQsFrame.setVisibility(View.VISIBLE);
+                mHomeControlsLayout.setVisibility(View.VISIBLE);
             } else {
                 mNotificationStackScroller.setVisibility(View.VISIBLE);
                 mQsFrame.setVisibility(View.GONE);
+                mHomeControlsLayout.setVisibility(View.GONE);
             }
         }
         return false;
@@ -1551,6 +1574,7 @@
         if (mKeyguardShowing && isQsSplitEnabled()) {
             mNotificationStackScroller.setVisibility(View.VISIBLE);
             mQsFrame.setVisibility(View.VISIBLE);
+            mHomeControlsLayout.setVisibility(View.GONE);
         }
 
         if (oldState == StatusBarState.KEYGUARD
@@ -2099,8 +2123,10 @@
                 t = (expandedHeight - panelHeightQsCollapsed)
                         / (panelHeightQsExpanded - panelHeightQsCollapsed);
             }
-            setQsExpansion(mQsMinExpansionHeight
-                    + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight));
+            float targetHeight = mQsMinExpansionHeight
+                    + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
+            setQsExpansion(targetHeight);
+            mHomeControlsLayout.setTranslationY(targetHeight);
         }
         updateExpandedHeight(expandedHeight);
         updateHeader();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 3508c90..ccb85fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -221,7 +221,7 @@
         mBiometricUnlockController = biometricUnlockController;
         mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
                 mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
-                mExpansionCallback, falsingManager);
+                mExpansionCallback, falsingManager, bypassController);
         mNotificationPanelView = notificationPanelView;
         notificationPanelView.addExpansionListener(this);
         mBypassController = bypassController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
index 0d6178b..f2c0434 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
@@ -97,7 +97,7 @@
         boolean allSimsMissing = true;
         CharSequence displayText = null;
 
-        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
+        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
         final int N = subs.size();
         for (int i = 0; i < N; i++) {
             int subId = subs.get(i).getSubscriptionId();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index 0044ca7..1ae1b97 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -70,15 +70,10 @@
     private static final CharSequence AIRPLANE_MODE_TEXT = "Airplane mode";
     private static final String TEST_CARRIER = "TEST_CARRIER";
     private static final String TEST_CARRIER_2 = "TEST_CARRIER_2";
-    private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24";
     private static final int TEST_CARRIER_ID = 1;
     private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(0, "", 0,
             TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
-            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
-            TEST_CARRIER_ID, 0);
-    private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(0, "", 0,
-            TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
-            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
+            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, null,
             TEST_CARRIER_ID, 0);
     private static final SubscriptionInfo TEST_SUBSCRIPTION_ROAMING = new SubscriptionInfo(0, "", 0,
             TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
@@ -125,7 +120,6 @@
                 mKeyguardUpdateMonitor);
         // This should not start listening on any of the real dependencies
         mCarrierTextController.setListening(mCarrierTextCallback);
-        mCarrierTextController.updateDisplayOpportunisticSubscriptionCarrierText(false);
     }
 
     @Test
@@ -134,7 +128,7 @@
         reset(mCarrierTextCallback);
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
         when(mKeyguardUpdateMonitor.getSimState(0)).thenReturn(IccCardConstants.State.READY);
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -154,7 +148,7 @@
         reset(mCarrierTextCallback);
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
         when(mKeyguardUpdateMonitor.getSimState(0)).thenReturn(IccCardConstants.State.READY);
         when(mKeyguardUpdateMonitor.getSimState(1)).thenReturn(
                 IccCardConstants.State.CARD_IO_ERROR);
@@ -178,7 +172,7 @@
     @Test
     public void testWrongSlots() {
         reset(mCarrierTextCallback);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(
                 new ArrayList<>());
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(
                 IccCardConstants.State.CARD_IO_ERROR);
@@ -192,7 +186,7 @@
     @Test
     public void testMoreSlotsThanSubs() {
         reset(mCarrierTextCallback);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(
                 new ArrayList<>());
 
         // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the
@@ -242,7 +236,7 @@
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION);
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -266,7 +260,7 @@
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION_ROAMING);
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -287,7 +281,7 @@
     @Test
     public void testCreateInfo_noSubscriptions() {
         reset(mCarrierTextCallback);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(
                 new ArrayList<>());
 
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -311,7 +305,7 @@
         list.add(TEST_SUBSCRIPTION);
         list.add(TEST_SUBSCRIPTION);
         when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -336,7 +330,7 @@
         when(mKeyguardUpdateMonitor.getSimState(anyInt()))
                 .thenReturn(IccCardConstants.State.READY)
                 .thenReturn(IccCardConstants.State.NOT_READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -361,7 +355,7 @@
         when(mKeyguardUpdateMonitor.getSimState(anyInt()))
                 .thenReturn(IccCardConstants.State.NOT_READY)
                 .thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
@@ -388,7 +382,7 @@
                 .thenReturn(IccCardConstants.State.READY)
                 .thenReturn(IccCardConstants.State.NOT_READY)
                 .thenReturn(IccCardConstants.State.READY);
-        when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list);
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
 
         ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
@@ -403,30 +397,6 @@
                 captor.getValue().carrierText);
     }
 
-    @Test
-    public void testCarrierText_GroupedSubWithOpportunisticCarrierText() {
-        reset(mCarrierTextCallback);
-        List<SubscriptionInfo> list = new ArrayList<>();
-        list.add(TEST_SUBSCRIPTION);
-        list.add(TEST_SUBSCRIPTION_2);
-        when(mKeyguardUpdateMonitor.getSimState(anyInt()))
-            .thenReturn(IccCardConstants.State.READY);
-
-        mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
-        mCarrierTextController.updateDisplayOpportunisticSubscriptionCarrierText(true);
-        when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
-
-        ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor =
-                ArgumentCaptor.forClass(
-                CarrierTextController.CarrierTextCallbackInfo.class);
-
-        mCarrierTextController.updateCarrierText();
-        mTestableLooper.processAllMessages();
-        verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
-
-        assertEquals(TEST_CARRIER_2, captor.getValue().carrierText);
-    }
-
     public static class TestCarrierTextController extends CarrierTextController {
         private KeyguardUpdateMonitor mKUM;
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index db6177a..a3cb6c0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -16,15 +16,18 @@
 
 package com.android.keyguard;
 
+import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
+import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -42,6 +45,7 @@
 import android.os.Bundle;
 import android.os.UserManager;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -62,6 +66,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 @SmallTest
@@ -73,7 +79,18 @@
 // new tests.
 @RunWithLooper(setAsMainLooper = true)
 public class KeyguardUpdateMonitorTest extends SysuiTestCase {
-
+    private static final String TEST_CARRIER = "TEST_CARRIER";
+    private static final String TEST_CARRIER_2 = "TEST_CARRIER_2";
+    private static final int TEST_CARRIER_ID = 1;
+    private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24";
+    private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(1, "", 0,
+            TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
+            TEST_CARRIER_ID, 0);
+    private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(2, "", 0,
+            TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "",
+            DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
+            TEST_CARRIER_ID, 0);
     @Mock
     private KeyguardUpdateMonitor.StrongAuthTracker mStrongAuthTracker;
     @Mock
@@ -92,6 +109,8 @@
     private DevicePolicyManager mDevicePolicyManager;
     @Mock
     private KeyguardBypassController mKeyguardBypassController;
+    @Mock
+    private SubscriptionManager mSubscriptionManager;
     private TestableLooper mTestableLooper;
     private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
@@ -119,6 +138,7 @@
         context.addMockSystemService(FaceManager.class, mFaceManager);
         context.addMockSystemService(UserManager.class, mUserManager);
         context.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
+        context.addMockSystemService(SubscriptionManager.class, mSubscriptionManager);
 
         mTestableLooper = TestableLooper.get(this);
         mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(context);
@@ -441,6 +461,22 @@
         assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
     }
 
+    @Test
+    public void testGetSubscriptionInfo_whenInGroupedSubWithOpportunistic() {
+        List<SubscriptionInfo> list = new ArrayList<>();
+        list.add(TEST_SUBSCRIPTION);
+        list.add(TEST_SUBSCRIPTION_2);
+        when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list);
+        mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged(
+                TEST_SUBSCRIPTION_2.getSubscriptionId());
+        mTestableLooper.processAllMessages();
+
+        List<SubscriptionInfo> listToVerify = mKeyguardUpdateMonitor
+                .getFilteredSubscriptionInfo(false);
+        assertThat(listToVerify.size()).isEqualTo(1);
+        assertThat(listToVerify.get(0)).isEqualTo(TEST_SUBSCRIPTION_2);
+    }
+
     private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
         int subscription = simInited
                 ? 1/* mock subid=1 */ : SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 907e695..cd60e47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
@@ -84,6 +85,8 @@
     @Mock
     private UnlockMethodCache mUnlockMethodCache;
     @Mock
+    private KeyguardBypassController mKeyguardBypassController;
+    @Mock
     private Handler mHandler;
 
     private KeyguardBouncer mBouncer;
@@ -98,7 +101,8 @@
         when(mKeyguardHostView.getHeight()).thenReturn(500);
         mBouncer = new KeyguardBouncer(getContext(), mViewMediatorCallback,
                 mLockPatternUtils, container, mDismissCallbackRegistry, mFalsingManager,
-                mExpansionCallback, mUnlockMethodCache, mKeyguardUpdateMonitor, mHandler) {
+                mExpansionCallback, mUnlockMethodCache, mKeyguardUpdateMonitor,
+                mKeyguardBypassController, mHandler) {
             @Override
             protected void inflateView() {
                 super.inflateView();
@@ -391,6 +395,15 @@
     }
 
     @Test
+    public void testShow_delaysIfFaceAuthIsRunning_unlessBypass() {
+        when(mUnlockMethodCache.isFaceAuthEnabled()).thenReturn(true);
+        when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
+        mBouncer.show(true /* reset */);
+
+        verify(mHandler, never()).postDelayed(any(), anyLong());
+    }
+
+    @Test
     public void testRegisterUpdateMonitorCallback() {
         verify(mKeyguardUpdateMonitor).registerCallback(any());
     }
diff --git a/packages/services/PacProcessor/Android.bp b/packages/services/PacProcessor/Android.bp
index 93b2d95..494a818 100644
--- a/packages/services/PacProcessor/Android.bp
+++ b/packages/services/PacProcessor/Android.bp
@@ -21,3 +21,9 @@
     certificate: "platform",
     jni_libs: ["libjni_pacprocessor"],
 }
+
+filegroup {
+    name: "PacProcessor-aidl-sources",
+    srcs: ["src/**/*.aidl"],
+    path: "src",
+}
diff --git a/packages/services/PacProcessor/com/android/net/IProxyService.aidl b/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl
similarity index 100%
rename from packages/services/PacProcessor/com/android/net/IProxyService.aidl
rename to packages/services/PacProcessor/src/com/android/net/IProxyService.aidl
diff --git a/packages/services/Proxy/Android.bp b/packages/services/Proxy/Android.bp
index 87aa763..d93c9f8 100644
--- a/packages/services/Proxy/Android.bp
+++ b/packages/services/Proxy/Android.bp
@@ -5,3 +5,9 @@
     certificate: "platform",
     privileged: true,
 }
+
+filegroup {
+    name: "ProxyHandler-aidl-sources",
+    srcs: ["src/**/*.aidl"],
+    path: "src",
+}
diff --git a/packages/services/Proxy/com/android/net/IProxyCallback.aidl b/packages/services/Proxy/src/com/android/net/IProxyCallback.aidl
similarity index 100%
rename from packages/services/Proxy/com/android/net/IProxyCallback.aidl
rename to packages/services/Proxy/src/com/android/net/IProxyCallback.aidl
diff --git a/packages/services/Proxy/com/android/net/IProxyPortListener.aidl b/packages/services/Proxy/src/com/android/net/IProxyPortListener.aidl
similarity index 100%
rename from packages/services/Proxy/com/android/net/IProxyPortListener.aidl
rename to packages/services/Proxy/src/com/android/net/IProxyPortListener.aidl
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 5e9c08b..1d936f2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -362,8 +362,10 @@
             if (!permissionGranted) {
                 return null;
             }
+            // TODO [Multi-Display] (b/134891479) :
+            // using correct display Id to replace DEFAULT_DISPLAY.
             List<AccessibilityWindowInfo> internalWindowList =
-                    mA11yWindowManager.getWindowListLocked();
+                    mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
             if (internalWindowList == null) {
                 return null;
             }
@@ -1309,23 +1311,25 @@
      */
     private void ensureWindowsAvailableTimed() {
         synchronized (mLock) {
-            if (mA11yWindowManager.getWindowListLocked() != null) {
+            // TODO [Multi-Display] (b/134891479) :
+            // using correct display Id to replace DEFAULT_DISPLAY.
+            if (mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY) != null) {
                 return;
             }
             // If we have no registered callback, update the state we
             // we may have to register one but it didn't happen yet.
-            if (!mA11yWindowManager.isTrackingWindowsLocked()) {
+            if (!mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)) {
                 // Invokes client change to make sure tracking window enabled.
                 mSystemSupport.onClientChangeLocked(false);
             }
             // We have no windows but do not care about them, done.
-            if (!mA11yWindowManager.isTrackingWindowsLocked()) {
+            if (!mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)) {
                 return;
             }
 
             // Wait for the windows with a timeout.
             final long startMillis = SystemClock.uptimeMillis();
-            while (mA11yWindowManager.getWindowListLocked() == null) {
+            while (mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY) == null) {
                 final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
                 final long remainMillis = WAIT_WINDOWS_TIMEOUT_MILLIS - elapsedMillis;
                 if (remainMillis <= 0) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 893e4e4..ddf5bbe 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -272,9 +272,9 @@
         mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this);
         mMainHandler = new MainHandler(mContext.getMainLooper());
         mGlobalActionPerformer = new GlobalActionPerformer(mContext, mWindowManagerService);
-        mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
         mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
                 mWindowManagerService, this, mSecurityPolicy, this);
+        mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
         mSecurityPolicy.setAccessibilityWindowManager(mA11yWindowManager);
 
         registerBroadcastReceivers();
@@ -580,9 +580,15 @@
             // Make sure clients receiving this event will be able to get the
             // current state of the windows as the window manager may be delaying
             // the computation for performance reasons.
-            // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY
-            if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
-                    && mA11yWindowManager.isTrackingWindowsLocked()) {
+            // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY.
+            boolean shouldComputeWindows = false;
+            synchronized (mLock) {
+                if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+                        && mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)) {
+                    shouldComputeWindows = true;
+                }
+            }
+            if (shouldComputeWindows) {
                 WindowManagerInternal wm = LocalServices.getService(WindowManagerInternal.class);
                 wm.computeWindowsForAccessibility(Display.DEFAULT_DISPLAY);
             }
@@ -1656,10 +1662,18 @@
             }
         }
 
-        if (observingWindows) {
-            mA11yWindowManager.startTrackingWindows();
-        } else {
-            mA11yWindowManager.stopTrackingWindows();
+        // Gets all valid displays and start tracking windows of each display if there is at least
+        // one bound service that can retrieve window content.
+        final ArrayList<Display> displays = getValidDisplayList();
+        for (int i = 0; i < displays.size(); i++) {
+            final Display display = displays.get(i);
+            if (display != null) {
+                if (observingWindows) {
+                    mA11yWindowManager.startTrackingWindows(display.getDisplayId());
+                } else {
+                    mA11yWindowManager.stopTrackingWindows(display.getDisplayId());
+                }
+            }
         }
     }
 
@@ -2559,6 +2573,7 @@
                     }
                 }
                 updateMagnificationLocked(userState);
+                updateWindowsForAccessibilityCallbackLocked(userState);
             }
         }
 
@@ -2586,6 +2601,7 @@
             if (mMagnificationController != null) {
                 mMagnificationController.onDisplayRemoved(displayId);
             }
+            mA11yWindowManager.stopTrackingWindows(displayId);
         }
 
         @Override
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index b88b24e..2032109 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -468,7 +468,13 @@
         }
     }
 
-    boolean hasPermission(String permission) {
+    /**
+     * Permission check to caller.
+     *
+     * @param permission The permission to check
+     * @return true if caller has permission
+     */
+    public boolean hasPermission(@NonNull String permission) {
         return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index c9efe36..82a593c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -79,16 +79,25 @@
     private final SparseArray<SparseArray<IBinder>> mWindowTokens = new SparseArray<>();
 
     private RemoteAccessibilityConnection mPictureInPictureActionReplacingConnection;
-
+    // There is only one active window in the system. It is updated when the top focused window
+    // of the top focused display changes and when we receive a TYPE_WINDOW_STATE_CHANGED event.
     private int mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
-    private int mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+    // There is only one top focused window in the system. It is updated when the window manager
+    // updates the window lists.
+    private int mTopFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
     private int mAccessibilityFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
     private long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
+    // The top focused display and window token updated with the callback of window lists change.
+    private int mTopFocusedDisplayId;
+    private IBinder mTopFocusedWindowToken;
+    // The display has the accessibility focused window currently.
+    private int mAccessibilityFocusedDisplayId = Display.INVALID_DISPLAY;
 
     private boolean mTouchInteractionInProgress;
 
-    // TO-DO [Multi-Display] : make DisplayWindowObserver to plural
-    private DisplayWindowsObserver mDisplayWindowsObserver;
+    /** List of Display Windows Observer, mapping from displayId -> DisplayWindowsObserver. */
+    private final SparseArray<DisplayWindowsObserver> mDisplayWindowsObservers =
+            new SparseArray<>();
 
     /**
      * This class implements {@link WindowManagerInternal.WindowsForAccessibilityCallback} to
@@ -243,6 +252,7 @@
                 for (int i = 0; i < windowCount; i++) {
                     AccessibilityWindowInfo window = mWindows.get(i);
                     if (window.getId() == windowId) {
+                        mAccessibilityFocusedDisplayId = mDisplayId;
                         window.setAccessibilityFocused(true);
                         mAccessibilityEventSender.sendAccessibilityEventForCurrentUserLocked(
                                 AccessibilityEvent.obtainWindowsChangedEvent(
@@ -318,17 +328,21 @@
          * Callbacks from window manager when there's an accessibility change in windows.
          *
          * @param forceSend Send the windows for accessibility even if they haven't changed.
+         * @param topFocusedDisplayId The display Id which has the top focused window.
+         * @param topFocusedWindowToken The window token of top focused window.
          * @param windows The windows for accessibility.
          */
         @Override
-        public void onWindowsForAccessibilityChanged(boolean forceSend,
-                @NonNull List<WindowInfo> windows) {
+        public void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId,
+                IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows) {
             synchronized (mLock) {
                 if (DEBUG) {
                     Slog.i(LOG_TAG, "Display Id = " + mDisplayId);
                     Slog.i(LOG_TAG, "Windows changed: " + windows);
                 }
                 if (shouldUpdateWindowsLocked(forceSend, windows)) {
+                    mTopFocusedDisplayId = topFocusedDisplayId;
+                    mTopFocusedWindowToken = topFocusedWindowToken;
                     cacheWindows(windows);
                     // Lets the policy update the focused and active windows.
                     updateWindowsLocked(mAccessibilityUserManager.getCurrentUserIdLocked(),
@@ -471,6 +485,7 @@
 
             final List<AccessibilityWindowInfo> oldWindowList = new ArrayList<>(mWindows);
             final SparseArray<AccessibilityWindowInfo> oldWindowsById = mA11yWindowInfoById.clone();
+            boolean shouldClearAccessibilityFocus = false;
 
             mWindows.clear();
             mA11yWindowInfoById.clear();
@@ -480,9 +495,25 @@
             }
             mWindowInfoById.clear();
             mHasWatchOutsideTouchWindow = false;
-            mFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
-            if (!mTouchInteractionInProgress) {
-                mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+
+            final int windowCount = windows.size();
+            final boolean isTopFocusedDisplay = mDisplayId == mTopFocusedDisplayId;
+            final boolean isAccessibilityFocusedDisplay =
+                    mDisplayId == mAccessibilityFocusedDisplayId;
+            // Modifies the value of top focused window, active window and a11y focused window
+            // only if this display is top focused display which has the top focused window.
+            if (isTopFocusedDisplay) {
+                if (windowCount > 0) {
+                    // Sets the top focus window by top focused window token.
+                    mTopFocusedWindowId = findWindowIdLocked(userId, mTopFocusedWindowToken);
+                } else {
+                    // Resets the top focus window when stopping tracking window of this display.
+                    mTopFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+                }
+                // The active window doesn't need to be reset if the touch operation is progressing.
+                if (!mTouchInteractionInProgress) {
+                    mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+                }
             }
 
             // If the active window goes away while the user is touch exploring we
@@ -492,17 +523,17 @@
             // filters out such events.
             boolean activeWindowGone = true;
 
-            final int windowCount = windows.size();
-
             // We'll clear accessibility focus if the window with focus is no longer visible to
-            // accessibility services
-            boolean shouldClearAccessibilityFocus =
-                    mAccessibilityFocusedWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+            // accessibility services.
+            if (isAccessibilityFocusedDisplay) {
+                shouldClearAccessibilityFocus = mAccessibilityFocusedWindowId
+                    != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+            }
             if (windowCount > 0) {
                 for (int i = 0; i < windowCount; i++) {
                     final WindowInfo windowInfo = windows.get(i);
                     final AccessibilityWindowInfo window;
-                    if (isTrackingWindowsLocked()) {
+                    if (mTrackingWindows) {
                         window = populateReportedWindowLocked(userId, windowInfo);
                     } else {
                         window = null;
@@ -513,9 +544,10 @@
                         window.setLayer(windowCount - 1 - window.getLayer());
 
                         final int windowId = window.getId();
-                        if (window.isFocused()) {
-                            mFocusedWindowId = windowId;
+                        if (window.isFocused() && isTopFocusedDisplay) {
                             if (!mTouchInteractionInProgress) {
+                                // This display is top one, and sets the focus window
+                                // as active window.
                                 mActiveWindowId = windowId;
                                 window.setActive(true);
                             } else if (windowId == mActiveWindowId) {
@@ -530,22 +562,28 @@
                         mWindowInfoById.put(windowId, WindowInfo.obtain(windowInfo));
                     }
                 }
-
-                if (mTouchInteractionInProgress && activeWindowGone) {
-                    mActiveWindowId = mFocusedWindowId;
-                }
-
-                // Focused window may change the active one, so set the
-                // active window once we decided which it is.
                 final int accessibilityWindowCount = mWindows.size();
-                for (int i = 0; i < accessibilityWindowCount; i++) {
-                    final AccessibilityWindowInfo window = mWindows.get(i);
-                    if (window.getId() == mActiveWindowId) {
-                        window.setActive(true);
+                if (isTopFocusedDisplay) {
+                    if (mTouchInteractionInProgress && activeWindowGone) {
+                        mActiveWindowId = mTopFocusedWindowId;
                     }
-                    if (window.getId() == mAccessibilityFocusedWindowId) {
-                        window.setAccessibilityFocused(true);
-                        shouldClearAccessibilityFocus = false;
+                    // Focused window may change the active one, so set the
+                    // active window once we decided which it is.
+                    for (int i = 0; i < accessibilityWindowCount; i++) {
+                        final AccessibilityWindowInfo window = mWindows.get(i);
+                        if (window.getId() == mActiveWindowId) {
+                            window.setActive(true);
+                        }
+                    }
+                }
+                if (isAccessibilityFocusedDisplay) {
+                    for (int i = 0; i < accessibilityWindowCount; i++) {
+                        final AccessibilityWindowInfo window = mWindows.get(i);
+                        if (window.getId() == mAccessibilityFocusedWindowId) {
+                            window.setAccessibilityFocused(true);
+                            shouldClearAccessibilityFocus = false;
+                            break;
+                        }
                     }
                 }
             }
@@ -788,42 +826,86 @@
         mAccessibilityEventSender = accessibilityEventSender;
         mSecurityPolicy = securityPolicy;
         mAccessibilityUserManager = accessibilityUserManager;
-        mDisplayWindowsObserver = new DisplayWindowsObserver(Display.DEFAULT_DISPLAY);
     }
 
     /**
-     * Starts tracking windows changes from window manager.
-     */
-    public void startTrackingWindows() {
-        synchronized (mLock) {
-            mDisplayWindowsObserver.startTrackingWindowsLocked();
-        }
-    }
-
-    /**
-     * Stops tracking windows changes from window manager, and clear all windows info.
-     */
-    public void stopTrackingWindows() {
-        synchronized (mLock) {
-            mDisplayWindowsObserver.stopTrackingWindowsLocked();
-        }
-    }
-
-    /**
-     * Returns true if windows changes tracking.
+     * Starts tracking windows changes from window manager for specified display.
      *
-     * @return true if windows changes tracking
+     * @param displayId The logical display id.
+     */
+    public void startTrackingWindows(int displayId) {
+        synchronized (mLock) {
+            DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId);
+            if (observer == null) {
+                observer = new DisplayWindowsObserver(displayId);
+            }
+            if (observer.isTrackingWindowsLocked()) {
+                return;
+            }
+            if (observer.startTrackingWindowsLocked()) {
+                mDisplayWindowsObservers.put(displayId, observer);
+            }
+        }
+    }
+
+    /**
+     * Stops tracking windows changes from window manager, and clear all windows info for specified
+     * display.
+     *
+     * @param displayId The logical display id.
+     */
+    public void stopTrackingWindows(int displayId) {
+        synchronized (mLock) {
+            final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId);
+            if (observer != null) {
+                observer.stopTrackingWindowsLocked();
+                mDisplayWindowsObservers.remove(displayId);
+            }
+        }
+    }
+
+    /**
+     * Checks if we are tracking windows on any display.
+     *
+     * @return {@code true} if the observer is tracking windows on any display,
+     * {@code false} otherwise.
      */
     public boolean isTrackingWindowsLocked() {
-        return mDisplayWindowsObserver.isTrackingWindowsLocked();
+        final int count = mDisplayWindowsObservers.size();
+        if (count > 0) {
+            return true;
+        }
+        return false;
     }
 
     /**
-     * Returns accessibility windows.
+     * Checks if we are tracking windows on specified display.
+     *
+     * @param displayId The logical display id.
+     * @return {@code true} if the observer is tracking windows on specified display,
+     * {@code false} otherwise.
+     */
+    public boolean isTrackingWindowsLocked(int displayId) {
+        final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId);
+        if (observer != null) {
+            return observer.isTrackingWindowsLocked();
+        }
+        return false;
+    }
+
+    /**
+     * Returns accessibility windows for specified display.
+     *
+     * @param displayId The logical display id.
+     * @return accessibility windows for specified display.
      */
     @Nullable
-    public List<AccessibilityWindowInfo> getWindowListLocked() {
-        return mDisplayWindowsObserver.getWindowListLocked();
+    public List<AccessibilityWindowInfo> getWindowListLocked(int displayId) {
+        final DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId);
+        if (observer != null) {
+            return observer.getWindowListLocked();
+        }
+        return null;
     }
 
     /**
@@ -841,6 +923,8 @@
             @NonNull IAccessibilityInteractionConnection connection, @NonNull String packageName,
             int userId) throws RemoteException {
         final int windowId;
+        boolean shouldComputeWindows = false;
+        final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken.asBinder());
         synchronized (mLock) {
             // We treat calls from a profile as if made by its parent as profiles
             // share the accessibility state of the parent. The call below
@@ -880,9 +964,14 @@
                             + " and  token: " + windowToken.asBinder());
                 }
             }
+
+            if (isTrackingWindowsLocked(displayId)) {
+                shouldComputeWindows = true;
+            }
         }
-        // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY
-        mWindowManagerInternal.computeWindowsForAccessibility(Display.DEFAULT_DISPLAY);
+        if (shouldComputeWindows) {
+            mWindowManagerInternal.computeWindowsForAccessibility(displayId);
+        }
         return windowId;
     }
 
@@ -1067,8 +1156,12 @@
      */
     public boolean computePartialInteractiveRegionForWindowLocked(int windowId,
             @NonNull Region outRegion) {
-        return mDisplayWindowsObserver.computePartialInteractiveRegionForWindowLocked(windowId,
-            outRegion);
+        final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId);
+        if (observer != null) {
+            return observer.computePartialInteractiveRegionForWindowLocked(windowId, outRegion);
+        }
+
+        return false;
     }
 
     /**
@@ -1101,8 +1194,8 @@
                 // windows are delivered.
                 synchronized (mLock) {
                     if (!isTrackingWindowsLocked()) {
-                        mFocusedWindowId = findFocusedWindowId(userId);
-                        if (windowId == mFocusedWindowId) {
+                        mTopFocusedWindowId = findFocusedWindowId(userId);
+                        if (windowId == mTopFocusedWindowId) {
                             mActiveWindowId = windowId;
                         }
                     }
@@ -1140,6 +1233,7 @@
                             && (mAccessibilityFocusedWindowId == windowId)
                             && (eventAction != AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS)) {
                         mAccessibilityFocusedWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+                        mAccessibilityFocusedDisplayId = Display.INVALID_DISPLAY;
                     }
                 }
             } break;
@@ -1172,7 +1266,7 @@
             // the active window before all hover accessibility events from
             // the touched window are delivered is fine.
             final int oldActiveWindow = mActiveWindowId;
-            setActiveWindowLocked(mFocusedWindowId);
+            setActiveWindowLocked(mTopFocusedWindowId);
 
             // If there is no service that can operate with interactive windows
             // then we keep the old behavior where a window loses accessibility
@@ -1212,7 +1306,14 @@
                             mActiveWindowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE));
 
             mActiveWindowId = windowId;
-            mDisplayWindowsObserver.setActiveWindowLocked(windowId);
+            // Goes through all windows for each display.
+            final int count = mDisplayWindowsObservers.size();
+            for (int i = 0; i < count; i++) {
+                final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
+                if (observer != null) {
+                    observer.setActiveWindowLocked(windowId);
+                }
+            }
         }
     }
 
@@ -1224,7 +1325,14 @@
                             WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED));
 
             mAccessibilityFocusedWindowId = windowId;
-            mDisplayWindowsObserver.setAccessibilityFocusedWindowLocked(windowId);
+            // Goes through all windows for each display.
+            final int count = mDisplayWindowsObservers.size();
+            for (int i = 0; i < count; i++) {
+                final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
+                if (observer != null) {
+                    observer.setAccessibilityFocusedWindowLocked(windowId);
+                }
+            }
         }
     }
 
@@ -1236,7 +1344,11 @@
      */
     @Nullable
     public AccessibilityWindowInfo findA11yWindowInfoByIdLocked(int windowId) {
-        return mDisplayWindowsObserver.findA11yWindowInfoByIdLocked(windowId);
+        final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId);
+        if (observer != null) {
+            return observer.findA11yWindowInfoByIdLocked(windowId);
+        }
+        return null;
     }
 
     /**
@@ -1247,7 +1359,11 @@
      */
     @Nullable
     public WindowInfo findWindowInfoByIdLocked(int windowId) {
-        return mDisplayWindowsObserver.findWindowInfoByIdLocked(windowId);
+        final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId);
+        if (observer != null) {
+            return observer.findWindowInfoByIdLocked(windowId);
+        }
+        return null;
     }
 
     /**
@@ -1259,7 +1375,7 @@
      */
     public int getFocusedWindowId(int focusType) {
         if (focusType == AccessibilityNodeInfo.FOCUS_INPUT) {
-            return mFocusedWindowId;
+            return mTopFocusedWindowId;
         } else if (focusType == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) {
             return mAccessibilityFocusedWindowId;
         }
@@ -1273,7 +1389,17 @@
      */
     @Nullable
     public AccessibilityWindowInfo getPictureInPictureWindowLocked() {
-        return mDisplayWindowsObserver.getPictureInPictureWindowLocked();
+        AccessibilityWindowInfo windowInfo = null;
+        final int count = mDisplayWindowsObservers.size();
+        for (int i = 0; i < count; i++) {
+            final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
+            if (observer != null) {
+                if ((windowInfo = observer.getPictureInPictureWindowLocked()) != null) {
+                    break;
+                }
+            }
+        }
+        return windowInfo;
     }
 
     /**
@@ -1313,10 +1439,13 @@
         final List<Integer> outsideWindowsIds;
         final List<RemoteAccessibilityConnection> connectionList = new ArrayList<>();
         synchronized (mLock) {
-            outsideWindowsIds =
-                mDisplayWindowsObserver.getWatchOutsideTouchWindowIdLocked(targetWindowId);
-            for (int i = 0; i < outsideWindowsIds.size(); i++) {
-                connectionList.add(getConnectionLocked(userId, outsideWindowsIds.get(i)));
+            final DisplayWindowsObserver observer =
+                    getDisplayWindowObserverByWindowIdLocked(targetWindowId);
+            if (observer != null) {
+                outsideWindowsIds = observer.getWatchOutsideTouchWindowIdLocked(targetWindowId);
+                for (int i = 0; i < outsideWindowsIds.size(); i++) {
+                    connectionList.add(getConnectionLocked(userId, outsideWindowsIds.get(i)));
+                }
             }
         }
         for (int i = 0; i < connectionList.size(); i++) {
@@ -1398,10 +1527,29 @@
         }
     }
 
+    private DisplayWindowsObserver getDisplayWindowObserverByWindowIdLocked(int windowId) {
+        final int count = mDisplayWindowsObservers.size();
+        for (int i = 0; i < count; i++) {
+            final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
+            if (observer != null) {
+                if (observer.findWindowInfoByIdLocked(windowId) != null) {
+                    return mDisplayWindowsObservers.get(observer.mDisplayId);
+                }
+            }
+        }
+        return null;
+    }
+
     /**
      * Dumps all {@link AccessibilityWindowInfo}s here.
      */
     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
-        mDisplayWindowsObserver.dumpLocked(fd, pw, args);
+        final int count = mDisplayWindowsObservers.size();
+        for (int i = 0; i < count; i++) {
+            final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
+            if (observer != null) {
+                observer.dumpLocked(fd, pw, args);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index 4facf4ea..4151c72 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -54,6 +54,10 @@
  * {@hide}
  */
 public abstract class SystemService {
+
+    // TODO(b/133242016) STOPSHIP: change to false before R ships
+    protected static final boolean DEBUG_USER = true;
+
     /*
      * Boot Phases
      */
@@ -150,7 +154,17 @@
     public void onBootPhase(int phase) {}
 
     /**
-     * @deprecated subclasses should extend {@link #onStartUser(int, int)} instead (which by default
+     * Checks if the service should be available for the given user.
+     *
+     * <p>By default returns {@code true}, but subclasses should extend for optimization, if they
+     * don't support some types (like headless system user).
+     */
+    public boolean isSupported(@NonNull UserInfo userInfo) {
+        return true;
+    }
+
+    /**
+     * @deprecated subclasses should extend {@link #onStartUser(UserInfo)} instead (which by default
      * calls this method).
      */
     @Deprecated
@@ -160,6 +174,9 @@
      * Called when a new user is starting, for system services to initialize any per-user
      * state they maintain for running users.
      *
+     * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this
+     * user.
+     *
      * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
      * referenced by {@link UserManagerService} and hence should not be modified.
      */
@@ -168,7 +185,7 @@
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onUnlockUser(int, int)} instead (which by
+     * @deprecated subclasses should extend {@link #onUnlockUser(UserInfo)} instead (which by
      * default calls this method).
      */
     @Deprecated
@@ -185,6 +202,9 @@
      * Code written inside system services should use
      * {@link UserManager#isUserUnlockingOrUnlocked(int)} to handle both of
      * these states.
+     * <p>
+     * This method is only called when the service {@link #isSupported(UserInfo) supports} this
+     * user.
      *
      * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
      * referenced by {@link UserManagerService} and hence should not be modified.
@@ -194,8 +214,8 @@
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onSwitchUser(int, int)} instead (which by
-     * default calls this method).
+     * @deprecated subclasses should extend {@link #onSwitchUser(UserInfo, UserInfo)} instead
+     * (which by default calls this method).
      */
     @Deprecated
     public void onSwitchUser(@UserIdInt int userHandle) {}
@@ -205,15 +225,21 @@
      * special behavior for whichever user is currently in the foreground.  This is called
      * before any application processes are aware of the new user.
      *
-     * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+     * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} either
+     * of the users ({@code from} or {@code to}).
+     *
+     * <b>NOTE: </b> both {@code from} and {@code to} are "live" objects
      * referenced by {@link UserManagerService} and hence should not be modified.
+     *
+     * @param from The information about the user being switched from.
+     * @param to The information about the user being switched from to.
      */
-    public void onSwitchUser(@NonNull UserInfo userInfo) {
-        onSwitchUser(userInfo.id);
+    public void onSwitchUser(@NonNull UserInfo from, @NonNull UserInfo to) {
+        onSwitchUser(to.id);
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onStopUser(int, int)} instead (which by default
+     * @deprecated subclasses should extend {@link #onStopUser(UserInfo)} instead (which by default
      * calls this method).
      */
     @Deprecated
@@ -225,6 +251,9 @@
      * broadcast to the user; it is a good place to stop making use of any resources of that
      * user (such as binding to a service running in the user).
      *
+     * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this
+     * user.
+     *
      * <p>NOTE: This is the last callback where the callee may access the target user's CE storage.
      *
      * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
@@ -235,7 +264,7 @@
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onCleanupUser(int, int)} instead (which by
+     * @deprecated subclasses should extend {@link #onCleanupUser(UserInfo)} instead (which by
      * default calls this method).
      */
     @Deprecated
@@ -246,8 +275,12 @@
      * state they maintain for running users.  This is called after all application process
      * teardown of the user is complete.
      *
+     * <p>This method is only called when the service {@link #isSupported(UserInfo) supports} this
+     * user.
+     *
      * <p>NOTE: When this callback is called, the CE storage for the target user may not be
-     * accessible already.  Use {@link #onCleanupUser} instead if you need to access the CE storage.
+     * accessible already.  Use {@link #onStopUser(UserInfo)} instead if you need to access the CE
+     * storage.
      *
      * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
      * referenced by {@link UserManagerService} and hence should not be modified.
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index b085946..c715798 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -23,6 +23,7 @@
 import android.os.Environment;
 import android.os.SystemClock;
 import android.os.Trace;
+import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.util.Slog;
 
@@ -41,8 +42,16 @@
  */
 public class SystemServiceManager {
     private static final String TAG = "SystemServiceManager";
+    private static final boolean DEBUG = false;
     private static final int SERVICE_CALL_WARN_TIME_MS = 50;
 
+    // Constants used on onUser(...)
+    private static final String START = "Start";
+    private static final String UNLOCK = "Unlock";
+    private static final String SWITCH = "Switch";
+    private static final String STOP = "Stop";
+    private static final String CLEANUP = "Cleanup";
+
     private static File sSystemDir;
     private final Context mContext;
     private boolean mSafeMode;
@@ -90,7 +99,6 @@
      * @return The service instance, never null.
      * @throws RuntimeException if the service fails to start.
      */
-    @SuppressWarnings("unchecked")
     public <T extends SystemService> T startService(Class<T> serviceClass) {
         try {
             final String name = serviceClass.getName();
@@ -212,64 +220,104 @@
      * Starts the given user.
      */
     public void startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) {
-        onUser(t, "Start", userHandle, (s, u) -> s.onStartUser(u));
+        onUser(t, START, userHandle);
     }
 
     /**
      * Unlocks the given user.
      */
     public void unlockUser(final @UserIdInt int userHandle) {
-        onUser("Unlock", userHandle, (s, u) -> s.onUnlockUser(u));
+        onUser(UNLOCK, userHandle);
     }
 
     /**
      * Switches to the given user.
      */
-    public void switchUser(final @UserIdInt int userHandle) {
-        onUser("Switch", userHandle, (s, u) -> s.onSwitchUser(u));
+    public void switchUser(final @UserIdInt int from, final @UserIdInt int to) {
+        onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, to, from);
     }
 
     /**
      * Stops the given user.
      */
     public void stopUser(final @UserIdInt int userHandle) {
-        onUser("Stop", userHandle, (s, u) -> s.onStopUser(u));
+        onUser(STOP, userHandle);
     }
 
     /**
      * Cleans up the given user.
      */
     public void cleanupUser(final @UserIdInt int userHandle) {
-        onUser("Cleanup", userHandle, (s, u) -> s.onCleanupUser(u));
+        onUser(CLEANUP, userHandle);
     }
 
-    private interface ServiceVisitor {
-        void visit(@NonNull SystemService service, @NonNull UserInfo userInfo);
-    }
-
-    private void onUser(@NonNull String onWhat, @UserIdInt int userHandle,
-            @NonNull ServiceVisitor visitor) {
-        onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userHandle, visitor);
+    private void onUser(@NonNull String onWhat, @UserIdInt int userHandle) {
+        onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userHandle);
     }
 
     private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
-            @UserIdInt int userHandle, @NonNull ServiceVisitor visitor) {
-        t.traceBegin("ssm." + onWhat + "User-" + userHandle);
-        Slog.i(TAG, "Calling on" + onWhat + "User u" + userHandle);
-        final UserInfo userInfo = getUserInfo(userHandle);
+            @UserIdInt int userHandle) {
+        onUser(t, onWhat, userHandle, UserHandle.USER_NULL);
+    }
+
+    private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
+            @UserIdInt int curUserId, @UserIdInt int prevUserId) {
+        t.traceBegin("ssm." + onWhat + "User-" + curUserId);
+        Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId);
+        final UserInfo curUserInfo = getUserInfo(curUserId);
+        final UserInfo prevUserInfo = prevUserId == UserHandle.USER_NULL ? null
+                : getUserInfo(prevUserId);
         final int serviceLen = mServices.size();
         for (int i = 0; i < serviceLen; i++) {
             final SystemService service = mServices.get(i);
             final String serviceName = service.getClass().getName();
-            t.traceBegin("ssm.on" + onWhat + "User-" + userHandle + " " + serviceName);
+            boolean supported = service.isSupported(curUserInfo);
+
+            // Must check if either curUser or prevUser is supported (for example, if switching from
+            // unsupported to supported, we still need to notify the services)
+            if (!supported && prevUserInfo != null) {
+                supported = service.isSupported(prevUserInfo);
+            }
+
+            if (!supported) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Skipping " + onWhat + "User-" + curUserId + " on service "
+                            + serviceName + " because it's not supported (curUser: "
+                            + curUserInfo + ", prevUser:" + prevUserInfo + ")");
+                } else {
+                    Slog.i(TAG,  "Skipping " + onWhat + "User-" + curUserId + " on "
+                            + serviceName);
+                }
+                continue;
+            }
+            t.traceBegin("ssm.on" + onWhat + "User-" + curUserId + " " + serviceName);
             long time = SystemClock.elapsedRealtime();
             try {
-                visitor.visit(service, userInfo);
+                switch (onWhat) {
+                    case SWITCH:
+                        service.onSwitchUser(prevUserInfo, curUserInfo);
+                        break;
+                    case START:
+                        service.onStartUser(curUserInfo);
+                        break;
+                    case UNLOCK:
+                        service.onUnlockUser(curUserInfo);
+                        break;
+                    case STOP:
+                        service.onStopUser(curUserInfo);
+                        break;
+                    case CLEANUP:
+                        service.onCleanupUser(curUserInfo);
+                        break;
+                    default:
+                        throw new IllegalArgumentException(onWhat + " what?");
+                }
             } catch (Exception ex) {
-                Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + userHandle
+                Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + curUserInfo
                         + " to service " + serviceName, ex);
             }
-            warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "on" + onWhat + "User ");
+            warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
+                    "on" + onWhat + "User-" + curUserId);
             t.traceEnd(); // what on service
         }
         t.traceEnd(); // main entry
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 0748279..9936d73 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -1188,6 +1188,7 @@
     private static boolean isNotification(int usageHint) {
         switch (usageHint) {
             case AudioAttributes.USAGE_NOTIFICATION:
+            case AudioAttributes.USAGE_NOTIFICATION_EVENT:
             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 5465309..c0af814 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -65,7 +65,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.MessageQueue;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.StrictMode;
@@ -101,11 +100,8 @@
 
 import dalvik.system.VMRuntime;
 
-import libcore.io.IoUtils;
-
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.ByteBuffer;
@@ -2087,10 +2083,10 @@
                     }
                 }
             }
-            if (lrui <= mLruProcessActivityStart) {
+            if (lrui < mLruProcessActivityStart) {
                 mLruProcessActivityStart--;
             }
-            if (lrui <= mLruProcessServiceStart) {
+            if (lrui < mLruProcessServiceStart) {
                 mLruProcessServiceStart--;
             }
             mLruProcesses.remove(lrui);
@@ -2622,7 +2618,7 @@
                         if (!moved) {
                             // Goes to the end of the group.
                             mLruProcesses.remove(i);
-                            mLruProcesses.add(endIndex - 1, subProc);
+                            mLruProcesses.add(endIndex, subProc);
                             if (DEBUG_LRU) Slog.d(TAG_LRU,
                                     "Moving " + subProc
                                             + " from position " + i + " to end of group @ "
@@ -2867,15 +2863,6 @@
                     pos--;
                 }
                 mLruProcesses.add(pos, app);
-                if (pos == mLruProcessActivityStart) {
-                    mLruProcessActivityStart++;
-                }
-                if (pos == mLruProcessServiceStart) {
-                    // Unless {@code #hasService} is implemented, currently the starting position
-                    // for activity and service are the same, so the incoming position may equal to
-                    // the starting position of service.
-                    mLruProcessServiceStart++;
-                }
                 // If this process is part of a group, need to pull up any other processes
                 // in that group to be with it.
                 int endIndex = pos - 1;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index d4ceb5a..b2c40ef 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2180,7 +2180,7 @@
                         BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
                         Integer.toString(msg.arg1), msg.arg1);
 
-                mInjector.getSystemServiceManager().switchUser(msg.arg1);
+                mInjector.getSystemServiceManager().switchUser(msg.arg2, msg.arg1);
                 break;
             case FOREGROUND_PROFILE_CHANGED_MSG:
                 dispatchForegroundProfileChanged(msg.arg1);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 4f33ebb0..c466640 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2020,6 +2020,10 @@
         @Override // Binder call
         public void resizeVirtualDisplay(IVirtualDisplayCallback callback,
                 int width, int height, int densityDpi) {
+            if (width <= 0 || height <= 0 || densityDpi <= 0) {
+                throw new IllegalArgumentException("width, height, and densityDpi must be "
+                        + "greater than 0");
+            }
             final long token = Binder.clearCallingIdentity();
             try {
                 resizeVirtualDisplayInternal(callback.asBinder(), width, height, densityDpi);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index 9e34018..e753a7b 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -43,7 +43,7 @@
  * Maintains a connection to a particular media route provider service.
  */
 final class MediaRoute2ProviderProxy implements ServiceConnection {
-    private static final String TAG = "MediaRoute2ProviderProxy";
+    private static final String TAG = "MediaRoute2Provider";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final Context mContext;
@@ -54,7 +54,6 @@
 
     private Callback mCallback;
 
-    //TODO: make it nonnull
     private MediaRoute2ProviderInfo mProviderInfo;
 
     // Connection state
@@ -246,13 +245,21 @@
         if (mActiveConnection != connection) {
             return;
         }
-        // Set a unique provider id for identifying providers.
-        mProviderInfo = new MediaRoute2ProviderInfo.Builder(info)
-                .setUniqueId(mUniqueId)
-                .build();
         if (DEBUG) {
             Slog.d(TAG, this + ": State changed ");
         }
+        setAndNotifyProviderInfo(info);
+    }
+
+    private void setAndNotifyProviderInfo(MediaRoute2ProviderInfo info) {
+        //TODO: check if info is not updated
+        if (info == null) {
+            mProviderInfo = null;
+        } else {
+            mProviderInfo = new MediaRoute2ProviderInfo.Builder(info)
+                .setUniqueId(mUniqueId)
+                .build();
+        }
         mHandler.post(mStateChanged);
     }
 
@@ -261,6 +268,7 @@
             mConnectionReady = false;
             mActiveConnection.dispose();
             mActiveConnection = null;
+            setAndNotifyProviderInfo(null);
         }
     }
 
@@ -337,7 +345,7 @@
             mHandler.post(() -> onConnectionDied(Connection.this));
         }
 
-        void postProviderUpdated(MediaRoute2ProviderInfo info) {
+        void postProviderInfoUpdated(MediaRoute2ProviderInfo info) {
             mHandler.post(() -> onProviderInfoUpdated(Connection.this, info));
         }
     }
@@ -354,10 +362,10 @@
         }
 
         @Override
-        public void notifyProviderInfoUpdated(MediaRoute2ProviderInfo info) {
+        public void updateProviderInfo(MediaRoute2ProviderInfo info) {
             Connection connection = mConnectionRef.get();
             if (connection != null) {
-                connection.postProviderUpdated(info);
+                connection.postProviderInfoUpdated(info);
             }
         }
     }
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 12137fe..043c834 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -33,6 +33,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -72,21 +73,20 @@
         mContext = context;
     }
 
-    public void registerClientAsUser(@NonNull IMediaRouter2Client client,
-            @NonNull String packageName, int userId) {
+    public void registerClient(@NonNull IMediaRouter2Client client,
+            @NonNull String packageName) {
         Objects.requireNonNull(client, "client must not be null");
 
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
-        final int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
-                false /*allowAll*/, true /*requireFull*/, "registerClientAsUser", packageName);
+        final int userId = UserHandle.getUserId(uid);
         final boolean trusted = mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
                 == PackageManager.PERMISSION_GRANTED;
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                registerClientLocked(client, uid, pid, packageName, resolvedUserId, trusted);
+                registerClientLocked(client, uid, pid, packageName, userId, trusted);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -106,20 +106,20 @@
         }
     }
 
-    public void registerManagerAsUser(@NonNull IMediaRouter2Manager manager,
-            @NonNull String packageName, int userId) {
+    public void registerManager(@NonNull IMediaRouter2Manager manager,
+            @NonNull String packageName) {
         Objects.requireNonNull(manager, "manager must not be null");
         //TODO: should check permission
         final boolean trusted = true;
 
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
-        final int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
-                false /*allowAll*/, true /*requireFull*/, "registerManagerAsUser", packageName);
+        final int userId = UserHandle.getUserId(uid);
+
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                registerManagerLocked(manager, uid, pid, packageName, resolvedUserId, trusted);
+                registerManagerLocked(manager, uid, pid, packageName, userId, trusted);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -254,6 +254,10 @@
 
             userRecord.mClientRecords.add(clientRecord);
             mAllClientRecords.put(binder, clientRecord);
+
+            userRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::notifyProviderInfosUpdatedToClient,
+                            userRecord.mHandler, client));
         }
     }
 
@@ -341,9 +345,9 @@
             userRecord.mManagerRecords.add(managerRecord);
             mAllManagerRecords.put(binder, managerRecord);
 
-            //TODO: remove this when it's unnecessary
-            // Sends published routes to newly added manager.
-            userRecord.mHandler.scheduleUpdateManagerState();
+            userRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::notifyProviderInfosUpdatedToManager,
+                            userRecord.mHandler, manager));
 
             final int count = userRecord.mClientRecords.size();
             for (int i = 0; i < count; i++) {
@@ -504,14 +508,14 @@
         private final WeakReference<MediaRouter2ServiceImpl> mServiceRef;
         private final UserRecord mUserRecord;
         private final MediaRoute2ProviderWatcher mWatcher;
-        private final ArrayList<IMediaRouter2Manager> mTempManagers = new ArrayList<>();
 
         //TODO: Make this thread-safe.
         private final ArrayList<MediaRoute2ProviderProxy> mMediaProviders =
                 new ArrayList<>();
+        private List<MediaRoute2ProviderInfo> mProviderInfos;
 
         private boolean mRunning;
-        private boolean mManagerStateUpdateScheduled;
+        private boolean mProviderInfosUpdateScheduled;
 
         UserHandler(MediaRouter2ServiceImpl service, UserRecord userRecord) {
             super(Looper.getMainLooper(), null, true);
@@ -553,14 +557,14 @@
         }
 
         private void updateProvider(MediaRoute2ProviderProxy provider) {
-            scheduleUpdateManagerState();
+            scheduleUpdateProviderInfos();
         }
 
         private void selectRoute(ClientRecord clientRecord, MediaRoute2Info route) {
             if (route != null) {
                 MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
                 if (provider == null) {
-                    Log.w(TAG, "Ignoring to select route of unknown provider " + route);
+                    Slog.w(TAG, "Ignoring to select route of unknown provider " + route);
                 } else {
                     provider.selectRoute(clientRecord.mPackageName, route.getId());
                 }
@@ -571,7 +575,7 @@
             if (route != null) {
                 MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
                 if (provider == null) {
-                    Log.w(TAG, "Ignoring to unselect route of unknown provider " + route);
+                    Slog.w(TAG, "Ignoring to unselect route of unknown provider " + route);
                 } else {
                     provider.unselectRoute(clientRecord.mPackageName, route.getId());
                 }
@@ -585,49 +589,71 @@
             }
         }
 
-        private void scheduleUpdateManagerState() {
-            if (!mManagerStateUpdateScheduled) {
-                mManagerStateUpdateScheduled = true;
-                sendMessage(PooledLambda.obtainMessage(UserHandler::updateManagerState, this));
+        private void scheduleUpdateProviderInfos() {
+            if (!mProviderInfosUpdateScheduled) {
+                mProviderInfosUpdateScheduled = true;
+                sendMessage(PooledLambda.obtainMessage(UserHandler::updateProviderInfos, this));
             }
         }
 
-        private void updateManagerState() {
-            mManagerStateUpdateScheduled = false;
+        private void updateProviderInfos() {
+            mProviderInfosUpdateScheduled = false;
 
             MediaRouter2ServiceImpl service = mServiceRef.get();
             if (service == null) {
                 return;
             }
-            //TODO: Consider using a member variable (like mTempManagers).
+            final List<IMediaRouter2Manager> managers = new ArrayList<>();
+            final List<IMediaRouter2Client> clients = new ArrayList<>();
             final List<MediaRoute2ProviderInfo> providers = new ArrayList<>();
             for (MediaRoute2ProviderProxy mediaProvider : mMediaProviders) {
                 final MediaRoute2ProviderInfo providerInfo =
                         mediaProvider.getProviderInfo();
                 if (providerInfo == null || !providerInfo.isValid()) {
-                    Log.w(TAG, "Ignoring invalid provider info : " + providerInfo);
+                    Slog.w(TAG, "Ignoring invalid provider info : " + providerInfo);
                 } else {
                     providers.add(providerInfo);
                 }
             }
+            mProviderInfos = providers;
 
+            synchronized (service.mLock) {
+                for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) {
+                    managers.add(managerRecord.mManager);
+                }
+                for (ClientRecord clientRecord : mUserRecord.mClientRecords) {
+                    clients.add(clientRecord.mClient);
+                }
+            }
+            for (IMediaRouter2Manager manager : managers) {
+                notifyProviderInfosUpdatedToManager(manager);
+            }
+            for (IMediaRouter2Client client : clients) {
+                notifyProviderInfosUpdatedToClient(client);
+            }
+        }
+
+        private void notifyProviderInfosUpdatedToClient(IMediaRouter2Client client) {
+            if (mProviderInfos == null) {
+                scheduleUpdateProviderInfos();
+                return;
+            }
             try {
-                synchronized (service.mLock) {
-                    final int count = mUserRecord.mManagerRecords.size();
-                    for (int i = 0; i < count; i++) {
-                        mTempManagers.add(mUserRecord.mManagerRecords.get(i).mManager);
-                    }
-                }
-                for (IMediaRouter2Manager tempManager : mTempManagers) {
-                    try {
-                        tempManager.notifyProviderInfosUpdated(providers);
-                    } catch (RemoteException ex) {
-                        Slog.w(TAG, "Failed to update manager state. Manager probably died.", ex);
-                    }
-                }
-            } finally {
-                // Clear the list in preparation for the next time.
-                mTempManagers.clear();
+                client.notifyProviderInfosUpdated(mProviderInfos);
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "Failed to notify provider infos updated. Client probably died.");
+            }
+        }
+
+        private void notifyProviderInfosUpdatedToManager(IMediaRouter2Manager manager) {
+            if (mProviderInfos == null) {
+                scheduleUpdateProviderInfos();
+                return;
+            }
+            try {
+                manager.notifyProviderInfosUpdated(mProviderInfos);
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "Failed to notify provider infos updated. Manager probably died.");
             }
         }
 
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index a43068b..2670cd8 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -435,12 +435,12 @@
 
     // Binder call
     @Override
-    public void registerClient2AsUser(IMediaRouter2Client client, String packageName, int userId) {
+    public void registerClient2(IMediaRouter2Client client, String packageName) {
         final int uid = Binder.getCallingUid();
         if (!validatePackageName(uid, packageName)) {
             throw new SecurityException("packageName must match the calling uid");
         }
-        mService2.registerClientAsUser(client, packageName, userId);
+        mService2.registerClient(client, packageName);
     }
 
     // Binder call
@@ -464,13 +464,12 @@
 
     // Binder call
     @Override
-    public void registerManagerAsUser(IMediaRouter2Manager manager,
-            String packageName, int userId) {
+    public void registerManager(IMediaRouter2Manager manager, String packageName) {
         final int uid = Binder.getCallingUid();
         if (!validatePackageName(uid, packageName)) {
             throw new SecurityException("packageName must match the calling uid");
         }
-        mService2.registerManagerAsUser(manager, packageName, userId);
+        mService2.registerManager(manager, packageName);
     }
 
     // Binder call
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 92ac1d4..d580bd6 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -74,6 +74,9 @@
     private static final String NON_BLOCKABLE_CHANNEL_DELIM = ":";
 
     @VisibleForTesting
+    static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
+
+    @VisibleForTesting
     static final String TAG_RANKING = "ranking";
     private static final String TAG_PACKAGE = "package";
     private static final String TAG_CHANNEL = "channel";
@@ -180,6 +183,7 @@
                                     // noop
                                 }
                             }
+                            boolean skipWarningLogged = false;
 
                             PackagePreferences r = getOrCreatePackagePreferencesLocked(name, uid,
                                     XmlUtils.readIntAttribute(
@@ -226,6 +230,14 @@
                                 }
                                 // Channels
                                 if (TAG_CHANNEL.equals(tagName)) {
+                                    if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
+                                        if (!skipWarningLogged) {
+                                            Slog.w(TAG, "Skipping further channels for " + r.pkg
+                                                    + "; app has too many");
+                                            skipWarningLogged = true;
+                                        }
+                                        continue;
+                                    }
                                     String id = parser.getAttributeValue(null, ATT_ID);
                                     String channelName = parser.getAttributeValue(null, ATT_NAME);
                                     int channelImportance = XmlUtils.readIntAttribute(
@@ -696,6 +708,10 @@
                 return needsPolicyFileChange;
             }
 
+            if (r.channels.size() >= NOTIFICATION_CHANNEL_COUNT_LIMIT) {
+                throw new IllegalStateException("Limit exceed; cannot create more channels");
+            }
+
             needsPolicyFileChange = true;
 
             if (channel.getImportance() < IMPORTANCE_NONE
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index a1b6d49..1da5bc6 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -38,6 +38,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.File;
@@ -263,7 +264,8 @@
                     populateAllPackagesCacheIfNeeded();
                     mContext.unregisterReceiver(this);
                 }
-            }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+            }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED), /* broadcastPermission */ null,
+                    BackgroundThread.getHandler());
         }
 
         private void populateAllPackagesCacheIfNeeded() {
diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
index 77bf930..712012d 100644
--- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
+++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java
@@ -24,20 +24,17 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
-import android.os.Debug;
 import android.provider.Settings;
-import android.telecom.TelecomManager;
 import android.text.TextUtils;
-import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.telephony.SmsApplication;
 import com.android.internal.util.CollectionUtils;
 import com.android.server.LocalServices;
 import com.android.server.role.RoleManagerService;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -67,14 +64,25 @@
     public List<String> getRoleHolders(@NonNull String roleName, @UserIdInt int userId) {
         switch (roleName) {
             case RoleManager.ROLE_ASSISTANT: {
-                String legacyAssistant = Settings.Secure.getStringForUser(
-                        mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId);
-                if (legacyAssistant == null || legacyAssistant.isEmpty()) {
-                    return Collections.emptyList();
+                String packageName;
+                String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                        Settings.Secure.ASSISTANT, userId);
+                // AssistUtils was using the default assistant app if Settings.Secure.ASSISTANT is
+                // null, while only an empty string means user selected "None".
+                if (setting != null) {
+                    if (!setting.isEmpty()) {
+                        ComponentName componentName = ComponentName.unflattenFromString(setting);
+                        packageName = componentName != null ? componentName.getPackageName() : null;
+                    } else {
+                        packageName = null;
+                    }
+                } else if (mContext.getPackageManager().isDeviceUpgrading()) {
+                    String defaultAssistant = mContext.getString(R.string.config_defaultAssistant);
+                    packageName = !TextUtils.isEmpty(defaultAssistant) ? defaultAssistant : null;
                 } else {
-                    return Collections.singletonList(
-                            ComponentName.unflattenFromString(legacyAssistant).getPackageName());
+                    packageName = null;
                 }
+                return CollectionUtils.singletonOrEmpty(packageName);
             }
             case RoleManager.ROLE_BROWSER: {
                 PackageManagerInternal packageManagerInternal = LocalServices.getService(
@@ -84,44 +92,36 @@
                 return CollectionUtils.singletonOrEmpty(packageName);
             }
             case RoleManager.ROLE_DIALER: {
-                String setting = Settings.Secure.getStringForUser(
-                        mContext.getContentResolver(),
+                String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                         Settings.Secure.DIALER_DEFAULT_APPLICATION, userId);
-                return CollectionUtils.singletonOrEmpty(!TextUtils.isEmpty(setting)
-                        ? setting
-                        : mContext.getSystemService(TelecomManager.class).getSystemDialerPackage());
+                String packageName;
+                if (!TextUtils.isEmpty(setting)) {
+                    packageName = setting;
+                } else if (mContext.getPackageManager().isDeviceUpgrading()) {
+                    // DefaultDialerManager was using the default dialer app if
+                    // Settings.Secure.DIALER_DEFAULT_APPLICATION is invalid.
+                    // TelecomManager.getSystemDialerPackage() won't work because it might not
+                    // be ready.
+                    packageName = mContext.getString(R.string.config_defaultDialer);
+                } else {
+                    packageName = null;
+                }
+                return CollectionUtils.singletonOrEmpty(packageName);
             }
             case RoleManager.ROLE_SMS: {
-                // Moved over from SmsApplication#getApplication
-                String result = Settings.Secure.getStringForUser(
-                        mContext.getContentResolver(),
+                String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                         Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
-                // TODO: STOPSHIP: Remove the following code once we read the value of
-                //  config_defaultSms in RoleControllerService.
-                if (result == null) {
-                    Collection<SmsApplication.SmsApplicationData> applications =
-                            SmsApplication.getApplicationCollectionAsUser(mContext, userId);
-                    SmsApplication.SmsApplicationData applicationData;
-                    String defaultPackage = mContext.getResources()
-                            .getString(com.android.internal.R.string.default_sms_application);
-                    applicationData =
-                            SmsApplication.getApplicationForPackage(applications, defaultPackage);
-
-                    if (applicationData == null) {
-                        // Are there any applications?
-                        if (applications.size() != 0) {
-                            applicationData =
-                                    (SmsApplication.SmsApplicationData) applications.toArray()[0];
-                        }
-                    }
-                    if (DEBUG) {
-                        Log.i(LOG_TAG, "Found default sms app: " + applicationData
-                                + " among: " + applications + " from " + Debug.getCallers(4));
-                    }
-                    SmsApplication.SmsApplicationData app = applicationData;
-                    result = app == null ? null : app.mPackageName;
+                String packageName;
+                if (!TextUtils.isEmpty(setting)) {
+                    packageName = setting;
+                } else if (mContext.getPackageManager().isDeviceUpgrading()) {
+                    // SmsApplication was using the default SMS app if
+                    // Settings.Secure.DIALER_DEFAULT_APPLICATION is invalid.
+                    packageName = mContext.getString(R.string.config_defaultSms);
+                } else {
+                    packageName = null;
                 }
-                return CollectionUtils.singletonOrEmpty(result);
+                return CollectionUtils.singletonOrEmpty(packageName);
             }
             case RoleManager.ROLE_HOME: {
                 PackageManager packageManager = mContext.getPackageManager();
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 89a5305..4c93564 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -142,11 +142,12 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG, "Unable to bind TextClassifierService at suggestSelection.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onSuggestSelection(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("suggestSelection",
                         () -> onSuggestSelection(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -166,11 +167,12 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG, "Unable to bind TextClassifierService at classifyText.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onClassifyText(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("classifyText",
                         () -> onClassifyText(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -190,11 +192,12 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG, "Unable to bind TextClassifierService at generateLinks.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onGenerateLinks(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("generateLinks",
                         () -> onGenerateLinks(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -214,7 +217,7 @@
             if (userState.isBoundLocked()) {
                 userState.mService.onSelectionEvent(sessionId, event);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("selectionEvent",
                         () -> onSelectionEvent(sessionId, event),
                         null /* onServiceFailure */, null /* binder */, this, userState));
             }
@@ -238,7 +241,7 @@
             if (userState.isBoundLocked()) {
                 userState.mService.onTextClassifierEvent(sessionId, event);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("textClassifierEvent",
                         () -> onTextClassifierEvent(sessionId, event),
                         null /* onServiceFailure */, null /* binder */, this, userState));
             }
@@ -258,11 +261,12 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG, "Unable to bind TextClassifierService at detectLanguage.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onDetectLanguage(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("detectLanguage",
                         () -> onDetectLanguage(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -282,11 +286,13 @@
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
             if (!userState.bindLocked()) {
+                Slog.d(LOG_TAG,
+                        "Unable to bind TextClassifierService at suggestConversationActions.");
                 callback.onFailure();
             } else if (userState.isBoundLocked()) {
                 userState.mService.onSuggestConversationActions(sessionId, request, callback);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("suggestConversationActions",
                         () -> onSuggestConversationActions(sessionId, request, callback),
                         callback::onFailure, callback.asBinder(), this, userState));
             }
@@ -309,7 +315,7 @@
                         classificationContext, sessionId);
                 mSessionUserIds.put(sessionId, userId);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
+                userState.mPendingRequests.add(new PendingRequest("createTextClassificationSession",
                         () -> onCreateTextClassificationSession(classificationContext, sessionId),
                         null /* onServiceFailure */, null /* binder */, this, userState));
             }
@@ -332,9 +338,10 @@
                 userState.mService.onDestroyTextClassificationSession(sessionId);
                 mSessionUserIds.remove(sessionId);
             } else {
-                userState.mPendingRequests.add(new PendingRequest(
-                        () -> onDestroyTextClassificationSession(sessionId),
-                        null /* onServiceFailure */, null /* binder */, this, userState));
+                userState.mPendingRequests.add(
+                        new PendingRequest("destroyTextClassificationSession",
+                                () -> onDestroyTextClassificationSession(sessionId),
+                                null /* onServiceFailure */, null /* binder */, this, userState));
             }
         }
     }
@@ -379,6 +386,7 @@
 
     private static final class PendingRequest implements IBinder.DeathRecipient {
 
+        @Nullable private final String mName;
         @Nullable private final IBinder mBinder;
         @NonNull private final Runnable mRequest;
         @Nullable private final Runnable mOnServiceFailure;
@@ -394,11 +402,12 @@
          * @param service
          * @param owningUser
          */
-        PendingRequest(
+        PendingRequest(@Nullable String name,
                 @NonNull ThrowingRunnable request, @Nullable ThrowingRunnable onServiceFailure,
                 @Nullable IBinder binder,
                 TextClassificationManagerService service,
                 UserState owningUser) {
+            mName = name;
             mRequest =
                     logOnFailure(Preconditions.checkNotNull(request), "handling pending request");
             mOnServiceFailure =
@@ -499,6 +508,8 @@
                     request.mRequest.run();
                 } else {
                     if (request.mOnServiceFailure != null) {
+                        Slog.d(LOG_TAG, "Unable to bind TextClassifierService for PendingRequest "
+                                + request.mName);
                         request.mOnServiceFailure.run();
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 26ca975..30a3aef 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -124,11 +124,11 @@
                 return false;
             }
 
-            final Display display = dc.getDisplay();
             if (mWindowsForAccessibilityObserver.get(displayId) != null) {
+                final Display display = dc.getDisplay();
                 if (display.getType() == Display.TYPE_VIRTUAL && dc.getParentWindow() != null) {
                     // The window observer of this embedded display had been set from
-                    // window manager after setting its parent window
+                    // window manager after setting its parent window.
                     return true;
                 } else {
                     throw new IllegalStateException(
@@ -136,9 +136,6 @@
                                     + displayId + " already set!");
                 }
             }
-            if (display.getType() == Display.TYPE_OVERLAY) {
-                return false;
-            }
             mWindowsForAccessibilityObserver.put(displayId,
                     new WindowsForAccessibilityObserver(mService, displayId, callback));
         } else {
@@ -287,9 +284,8 @@
     }
 
     public boolean hasCallbacksLocked() {
-        // TODO: support multi-display for windows observer
         return (mDisplayMagnifiers.size() > 0
-                || mWindowsForAccessibilityObserver != null);
+                || mWindowsForAccessibilityObserver.size() > 0);
     }
 
     public void setForceShowMagnifiableBoundsLocked(int displayId, boolean show) {
@@ -1158,15 +1154,15 @@
             }
 
             List<WindowInfo> windows = new ArrayList<>();
+            final int topFocusedDisplayId;
+            IBinder topFocusedWindowToken = null;
 
             synchronized (mService.mGlobalLock) {
-                // Do not send the windows if there is no current focus as
+                // Do not send the windows if there is no top focus as
                 // the window manager is still looking for where to put it.
                 // We will do the work when we get a focus change callback.
-                // TODO [Multi-Display] : only checks top focused window
-                if (!isCurrentFocusWindowOnDefaultDisplay()) {
-                    return;
-                }
+                final WindowState topFocusedWindowState = getTopFocusWindow();
+                if (topFocusedWindowState == null) return;
 
                 final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
                 if (dc == null) {
@@ -1229,9 +1225,13 @@
 
                 visibleWindows.clear();
                 addedWindows.clear();
-            }
 
-            mCallback.onWindowsForAccessibilityChanged(forceSend, windows);
+                // Gets the top focused display Id and window token for supporting multi-display.
+                topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId();
+                topFocusedWindowToken = topFocusedWindowState.mClient.asBinder();
+            }
+            mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
+                    topFocusedWindowToken, windows);
 
             // Recycle the windows as we do not need them.
             clearAndRecycleWindows(windows);
@@ -1410,22 +1410,9 @@
             }
             return displayParentWindow;
         }
-        // TODO [Multi-Display] : only checks top focused window
-        private boolean isCurrentFocusWindowOnDefaultDisplay() {
-            final WindowState focusedWindow =
-                    mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus;
-            if (focusedWindow == null) {
-                return false;
-            }
 
-            final WindowState rootDisplayParentWindow = findRootDisplayParentWindow(focusedWindow);
-            if (!focusedWindow.isDefaultDisplay()
-                    && (rootDisplayParentWindow == null
-                    || !rootDisplayParentWindow.isDefaultDisplay())) {
-                return false;
-            }
-
-            return true;
+        private WindowState getTopFocusWindow() {
+            return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus;
         }
 
         private class MyHandler extends Handler {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 563a1fed..fc36e99 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -106,6 +106,7 @@
 import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
 import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
 import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
 import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
@@ -115,13 +116,13 @@
 import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.wm.ActivityStack.LAUNCH_TICK;
-import static com.android.server.wm.ActivityStack.LAUNCH_TICK_MSG;
-import static com.android.server.wm.ActivityStack.PAUSE_TIMEOUT_MSG;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG;
 import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
@@ -133,6 +134,8 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
@@ -177,8 +180,10 @@
 import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityRelaunchItem;
+import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.ClientTransactionItem;
+import android.app.servertransaction.DestroyActivityItem;
 import android.app.servertransaction.MoveToDisplayItem;
 import android.app.servertransaction.MultiWindowModeChangeItem;
 import android.app.servertransaction.NewIntentItem;
@@ -198,12 +203,12 @@
 import android.graphics.Bitmap;
 import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
@@ -260,6 +265,8 @@
  */
 final class ActivityRecord extends ConfigurationContainer {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM;
+    private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
+    private static final String TAG_APP = TAG + POSTFIX_APP;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
     private static final String TAG_CONTAINERS = TAG + POSTFIX_CONTAINERS;
     private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
@@ -282,6 +289,9 @@
     private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
     static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
 
+    // How many activities have to be scheduled to stop to force a stop pass.
+    private static final int MAX_STOPPING_TO_FORCE = 3;
+
     final ActivityTaskManagerService mAtmService; // owner
     final IApplicationToken.Stub appToken; // window manager token
     // TODO: Remove after unification
@@ -1837,14 +1847,12 @@
         final ActivityStack stack = getActivityStack();
         final boolean notFocusedStack = stack != mRootActivityContainer.getTopDisplayFocusedStack();
         if (isVisible && next != null && !next.nowVisible) {
-            if (!mStackSupervisor.mStoppingActivities.contains(this)) {
-                getActivityStack().addToStopping(this, false /* scheduleIdle */,
-                        false /* idleDelayed */, "finishCurrentActivityLocked");
-            }
+            addToStopping(false /* scheduleIdle */, false /* idleDelayed */,
+                    "completeFinishing");
             if (DEBUG_STATES) {
                 Slog.v(TAG_STATES, "Moving to STOPPING: " + this + " (finish requested)");
             }
-            setState(STOPPING, "finishCurrentActivityLocked");
+            setState(STOPPING, "completeFinishing");
             if (notFocusedStack) {
                 mRootActivityContainer.ensureVisibilityAndConfig(next, getDisplayId(),
                         false /* markFrozenIfConfigChanged */, true /* deferResume */);
@@ -1890,8 +1898,8 @@
         }
         makeFinishingLocked();
 
-        boolean activityRemoved = getActivityStack().destroyActivityLocked(this,
-                true /* removeFromApp */, "finish-imm:" + reason);
+        final boolean activityRemoved = destroyImmediately(true /* removeFromApp */,
+                "finish-imm:" + reason);
 
         // If the display does not have running activity, the configuration may need to be
         // updated for restoring original orientation of the display.
@@ -1920,6 +1928,183 @@
         mRootActivityContainer.resumeFocusedStacksTopActivities();
     }
 
+    /**
+     * Destroy the current CLIENT SIDE instance of an activity. This may be called both when
+     * actually finishing an activity, or when performing a configuration switch where we destroy
+     * the current client-side object but then create a new client-side object for this same
+     * HistoryRecord.
+     * Normally the server-side record will be removed when the client reports back after
+     * destruction. If, however, at this point there is no client process attached, the record will
+     * removed immediately.
+     */
+    boolean destroyImmediately(boolean removeFromApp, String reason) {
+        if (DEBUG_SWITCH || DEBUG_CLEANUP) {
+            Slog.v(TAG_SWITCH, "Removing activity from " + reason + ": token=" + this
+                    + ", app=" + (hasProcess() ? app.mName : "(null)"));
+        }
+
+        if (isState(DESTROYING, DESTROYED)) {
+            if (DEBUG_STATES) {
+                Slog.v(TAG_STATES, "activity " + this + " already destroying."
+                        + "skipping request with reason:" + reason);
+            }
+            return false;
+        }
+
+        EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, mUserId,
+                System.identityHashCode(this), getTaskRecord().taskId, shortComponentName, reason);
+
+        boolean removedFromHistory = false;
+
+        cleanUp(false /* cleanServices */, false /* setState */);
+
+        final ActivityStack stack = getActivityStack();
+        final boolean hadApp = hasProcess();
+
+        if (hadApp) {
+            if (removeFromApp) {
+                app.removeActivity(this);
+                if (!app.hasActivities()) {
+                    mAtmService.clearHeavyWeightProcessIfEquals(app);
+                    // Update any services we are bound to that might care about whether
+                    // their client may have activities.
+                    // No longer have activities, so update LRU list and oom adj.
+                    app.updateProcessInfo(true /* updateServiceConnectionActivities */,
+                            false /* activityChange */, true /* updateOomAdj */);
+                }
+            }
+
+            boolean skipDestroy = false;
+
+            try {
+                if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + this);
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                        DestroyActivityItem.obtain(finishing, configChangeFlags));
+            } catch (Exception e) {
+                // We can just ignore exceptions here...  if the process has crashed, our death
+                // notification will clean things up.
+                if (finishing) {
+                    removeFromHistory(reason + " exceptionInScheduleDestroy");
+                    removedFromHistory = true;
+                    skipDestroy = true;
+                }
+            }
+
+            nowVisible = false;
+
+            // If the activity is finishing, we need to wait on removing it from the list to give it
+            // a chance to do its cleanup.  During that time it may make calls back with its token
+            // so we need to be able to find it on the list and so we don't want to remove it from
+            // the list yet.  Otherwise, we can just immediately put it in the destroyed state since
+            // we are not removing it from the list.
+            if (finishing && !skipDestroy) {
+                if (DEBUG_STATES) {
+                    Slog.v(TAG_STATES, "Moving to DESTROYING: " + this + " (destroy requested)");
+                }
+                setState(DESTROYING,
+                        "destroyActivityLocked. finishing and not skipping destroy");
+                stack.scheduleDestroyTimeoutForActivity(this);
+            } else {
+                if (DEBUG_STATES) {
+                    Slog.v(TAG_STATES, "Moving to DESTROYED: " + this + " (destroy skipped)");
+                }
+                setState(DESTROYED,
+                        "destroyActivityLocked. not finishing or skipping destroy");
+                if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + this);
+                app = null;
+            }
+        } else {
+            // Remove this record from the history.
+            if (finishing) {
+                removeFromHistory(reason + " hadNoApp");
+                removedFromHistory = true;
+            } else {
+                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + this + " (no app)");
+                setState(DESTROYED, "destroyActivityLocked. not finishing and had no app");
+            }
+        }
+
+        configChangeFlags = 0;
+
+        if (!stack.removeActivityFromLRUList(this) && hadApp) {
+            Slog.w(TAG, "Activity " + this + " being finished, but not in LRU list");
+        }
+
+        return removedFromHistory;
+    }
+
+    boolean safelyDestroy(String reason) {
+        if (isDestroyable()) {
+            if (DEBUG_SWITCH) {
+                final ActivityStack stack = getActivityStack();
+                Slog.v(TAG_SWITCH, "Safely destroying " + this + " in state " + getState()
+                        + " resumed=" + stack.mResumedActivity
+                        + " pausing=" + stack.mPausingActivity
+                        + " for reason " + reason);
+            }
+            return destroyImmediately(true /* removeFromApp */, reason);
+        }
+        return false;
+    }
+
+    /** Note: call {@link #cleanUp(boolean, boolean)} before this method. */
+    void removeFromHistory(String reason) {
+        finishActivityResults(Activity.RESULT_CANCELED, null /* resultData */);
+        makeFinishingLocked();
+        if (ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE) {
+            Slog.i(TAG_ADD_REMOVE, "Removing activity " + this + " from stack callers="
+                    + Debug.getCallers(5));
+        }
+
+        takeFromHistory();
+        final ActivityStack stack = getActivityStack();
+        stack.removeTimeoutsForActivity(this);
+        if (DEBUG_STATES) {
+            Slog.v(TAG_STATES, "Moving to DESTROYED: " + this + " (removed from history)");
+        }
+        setState(DESTROYED, "removeFromHistory");
+        if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + this);
+        app = null;
+        removeWindowContainer();
+        final TaskRecord task = getTaskRecord();
+        final boolean lastActivity = task.removeActivity(this);
+        // If we are removing the last activity in the task, not including task overlay activities,
+        // then fall through into the block below to remove the entire task itself
+        final boolean onlyHasTaskOverlays =
+                task.onlyHasTaskOverlayActivities(false /* excludingFinishing */);
+
+        if (lastActivity || onlyHasTaskOverlays) {
+            if (DEBUG_STATES) {
+                Slog.i(TAG, "removeFromHistory: last activity removed from " + this
+                        + " onlyHasTaskOverlays=" + onlyHasTaskOverlays);
+            }
+
+            // The following block can be executed multiple times if there is more than one overlay.
+            // {@link ActivityStackSupervisor#removeTaskByIdLocked} handles this by reverse lookup
+            // of the task by id and exiting early if not found.
+            if (onlyHasTaskOverlays) {
+                // When destroying a task, tell the supervisor to remove it so that any activity it
+                // has can be cleaned up correctly. This is currently the only place where we remove
+                // a task with the DESTROYING mode, so instead of passing the onlyHasTaskOverlays
+                // state into removeTask(), we just clear the task here before the other residual
+                // work.
+                // TODO: If the callers to removeTask() changes such that we have multiple places
+                //       where we are destroying the task, move this back into removeTask()
+                mStackSupervisor.removeTaskByIdLocked(task.taskId, false /* killProcess */,
+                        !REMOVE_FROM_RECENTS, PAUSE_IMMEDIATELY, reason);
+            }
+
+            // We must keep the task around until all activities are destroyed. The following
+            // statement will only execute once since overlays are also considered activities.
+            if (lastActivity) {
+                stack.removeTask(task, reason, REMOVE_TASK_MODE_DESTROYING);
+            }
+        }
+
+        cleanUpActivityServices();
+        removeUriPermissionsLocked();
+    }
+
     void makeFinishingLocked() {
         if (finishing) {
             return;
@@ -1934,6 +2119,96 @@
         }
     }
 
+    /**
+     * This method is to only be called from the client via binder when the activity is destroyed
+     * AND finished.
+     */
+    void destroyed(String reason) {
+        getActivityStack().removeDestroyTimeoutForActivity(this);
+
+        if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + this);
+
+        if (!isState(DESTROYING, DESTROYED)) {
+            throw new IllegalStateException(
+                    "Reported destroyed for activity that is not destroying: r=" + this);
+        }
+
+        if (isInStackLocked()) {
+            cleanUp(true /* cleanServices */, false /* setState */);
+            removeFromHistory(reason);
+        }
+
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
+    }
+
+    /**
+     * Perform the common clean-up of an activity record.  This is called both as part of
+     * destroyActivityLocked() (when destroying the client-side representation) and cleaning things
+     * up as a result of its hosting processing going away, in which case there is no remaining
+     * client-side state to destroy so only the cleanup here is needed.
+     *
+     * Note: Call before {@link #removeFromHistory(String)}.
+     */
+    void cleanUp(boolean cleanServices, boolean setState) {
+        final ActivityStack stack = getActivityStack();
+        stack.onActivityRemovedFromStack(this);
+
+        deferRelaunchUntilPaused = false;
+        frozenBeforeDestroy = false;
+
+        if (setState) {
+            setState(DESTROYED, "cleanUp");
+            if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + this);
+            app = null;
+        }
+
+        // Inform supervisor the activity has been removed.
+        mStackSupervisor.cleanupActivity(this);
+
+        // Remove any pending results.
+        if (finishing && pendingResults != null) {
+            for (WeakReference<PendingIntentRecord> apr : pendingResults) {
+                PendingIntentRecord rec = apr.get();
+                if (rec != null) {
+                    mAtmService.mPendingIntentController.cancelIntentSender(rec,
+                            false /* cleanActivity */);
+                }
+            }
+            pendingResults = null;
+        }
+
+        if (cleanServices) {
+            cleanUpActivityServices();
+        }
+
+        // Get rid of any pending idle timeouts.
+        stack.removeTimeoutsForActivity(this);
+        // Clean-up activities are no longer relaunching (e.g. app process died). Notify window
+        // manager so it can update its bookkeeping.
+        mAtmService.mWindowManager.notifyAppRelaunchesCleared(appToken);
+    }
+
+    /**
+     * Perform clean-up of service connections in an activity record.
+     */
+    private void cleanUpActivityServices() {
+        if (mServiceConnectionsHolder == null) {
+            return;
+        }
+        // Throw away any services that have been bound by this activity.
+        mServiceConnectionsHolder.disconnectActivityFromServices();
+    }
+
+    void logStartActivity(int tag, TaskRecord task) {
+        final Uri data = intent.getData();
+        final String strData = data != null ? data.toSafeString() : null;
+
+        EventLog.writeEvent(tag,
+                mUserId, System.identityHashCode(this), task.taskId,
+                shortComponentName, intent.getAction(),
+                intent.getType(), strData, intent.getFlags());
+    }
+
     UriPermissionOwner getUriPermissionsLocked() {
         if (uriPermissions == null) {
             uriPermissions = new UriPermissionOwner(mAtmService.mUgmInternal, this);
@@ -1970,6 +2245,33 @@
         }
     }
 
+    void sendResult(int callingUid, String resultWho, int requestCode, int resultCode,
+            Intent data) {
+        if (callingUid > 0) {
+            mAtmService.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
+                    data, getUriPermissionsLocked(), mUserId);
+        }
+
+        if (DEBUG_RESULTS) {
+            Slog.v(TAG, "Send activity result to " + this
+                    + " : who=" + resultWho + " req=" + requestCode
+                    + " res=" + resultCode + " data=" + data);
+        }
+        if (isState(RESUMED) && attachedToProcess()) {
+            try {
+                final ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
+                list.add(new ResultInfo(resultWho, requestCode, resultCode, data));
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                        ActivityResultItem.obtain(list));
+                return;
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception thrown sending result to " + this, e);
+            }
+        }
+
+        addResultLocked(null /* from */, resultWho, requestCode, resultCode, data);
+    }
+
     private void addNewIntentLocked(ReferrerIntent intent) {
         if (newIntents == null) {
             newIntents = new ArrayList<>();
@@ -2449,6 +2751,65 @@
         }
     }
 
+    void makeInvisible() {
+        if (!visible) {
+            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + this);
+            return;
+        }
+        // Now for any activities that aren't visible to the user, make sure they no longer are
+        // keeping the screen frozen.
+        if (DEBUG_VISIBILITY) {
+            Slog.v(TAG_VISIBILITY, "Making invisible: " + this + ", state=" + getState());
+        }
+        try {
+            final boolean canEnterPictureInPicture = checkEnterPictureInPictureState(
+                    "makeInvisible", true /* beforeStopping */);
+            // Defer telling the client it is hidden if it can enter Pip and isn't current paused,
+            // stopped or stopping. This gives it a chance to enter Pip in onPause().
+            // TODO: There is still a question surrounding activities in multi-window mode that want
+            // to enter Pip after they are paused, but are still visible. I they should be okay to
+            // enter Pip in those cases, but not "auto-Pip" which is what this condition covers and
+            // the current contract for "auto-Pip" is that the app should enter it before onPause
+            // returns. Just need to confirm this reasoning makes sense.
+            final boolean deferHidingClient = canEnterPictureInPicture
+                    && !isState(STOPPING, STOPPED, PAUSED);
+            setDeferHidingClient(deferHidingClient);
+            setVisible(false);
+
+            switch (getState()) {
+                case STOPPING:
+                case STOPPED:
+                    if (attachedToProcess()) {
+                        if (DEBUG_VISIBILITY) {
+                            Slog.v(TAG_VISIBILITY, "Scheduling invisibility: " + this);
+                        }
+                        mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(),
+                                appToken, WindowVisibilityItem.obtain(false /* showWindow */));
+                    }
+
+                    // Reset the flag indicating that an app can enter picture-in-picture once the
+                    // activity is hidden
+                    supportsEnterPipOnTaskSwitch = false;
+                    break;
+
+                case INITIALIZING:
+                case RESUMED:
+                case PAUSING:
+                case PAUSED:
+                case STARTED:
+                    addToStopping(true /* scheduleIdle */,
+                            canEnterPictureInPicture /* idleDelayed */, "makeInvisible");
+                    break;
+
+                default:
+                    break;
+            }
+        } catch (Exception e) {
+            // Just skip on any failure; we'll make it visible when it next restarts.
+            Slog.w(TAG, "Exception thrown making hidden: " + intent.getComponent(), e);
+        }
+    }
+
     /**
      * Make activity resumed or paused if needed.
      * @param activeActivity an activity that is resumed or just completed pause action.
@@ -2639,13 +3000,75 @@
         }
     }
 
+    void stopIfPossible() {
+        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + this);
+        final ActivityStack stack = getActivityStack();
+        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
+                || (info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0) {
+            if (!finishing) {
+                if (!stack.shouldSleepActivities()) {
+                    if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + this);
+                    if (finishIfPossible("stop-no-history", false /* oomAdj */)
+                            != FINISH_RESULT_CANCELLED) {
+                        // {@link adjustFocusedActivityStack} must have been already called.
+                        resumeKeyDispatchingLocked();
+                        return;
+                    }
+                } else {
+                    if (DEBUG_STATES) {
+                        Slog.d(TAG_STATES, "Not finishing noHistory " + this
+                                + " on stop because we're just sleeping");
+                    }
+                }
+            }
+        }
+
+        if (!attachedToProcess()) {
+            return;
+        }
+        stack.adjustFocusedActivityStack(this, "stopActivity");
+        resumeKeyDispatchingLocked();
+        try {
+            stopped = false;
+            if (DEBUG_STATES) {
+                Slog.v(TAG_STATES, "Moving to STOPPING: " + this + " (stop requested)");
+            }
+            setState(STOPPING, "stopIfPossible");
+            if (DEBUG_VISIBILITY) {
+                Slog.v(TAG_VISIBILITY, "Stopping visible=" + visible + " for " + this);
+            }
+            if (!visible) {
+                setVisible(false);
+            }
+            EventLogTags.writeAmStopActivity(
+                    mUserId, System.identityHashCode(this), shortComponentName);
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                    StopActivityItem.obtain(visible, configChangeFlags));
+            if (stack.shouldSleepOrShutDownActivities()) {
+                setSleeping(true);
+            }
+            stack.scheduleStopTimeoutForActivity(this);
+        } catch (Exception e) {
+            // Maybe just ignore exceptions here...  if the process has crashed, our death
+            // notification will clean things up.
+            Slog.w(TAG, "Exception thrown during pause", e);
+            // Just in case, assume it to be stopped.
+            stopped = true;
+            if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + this);
+            setState(STOPPED, "stopIfPossible");
+            if (deferRelaunchUntilPaused) {
+                destroyImmediately(true /* removeFromApp */, "stop-except");
+            }
+        }
+    }
+
     final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
             CharSequence description) {
         final ActivityStack stack = getActivityStack();
         final boolean isStopping = mState == STOPPING;
         if (!isStopping && mState != RESTARTING_PROCESS) {
             Slog.i(TAG, "Activity reported stop, but no longer stopping: " + this);
-            stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
+            stack.removeStopTimeoutForActivity(this);
             return;
         }
         if (newPersistentState != null) {
@@ -2663,7 +3086,7 @@
         if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + mIcicle);
         if (!stopped) {
             if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPED: " + this + " (stop complete)");
-            stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
+            stack.removeStopTimeoutForActivity(this);
             stopped = true;
             if (isStopping) {
                 setState(STOPPED, "activityStoppedLocked");
@@ -2677,7 +3100,7 @@
                 clearOptionsLocked();
             } else {
                 if (deferRelaunchUntilPaused) {
-                    stack.destroyActivityLocked(this, true /* removeFromApp */, "stop-config");
+                    destroyImmediately(true /* removeFromApp */, "stop-config");
                     mRootActivityContainer.resumeFocusedStacksTopActivities();
                 } else {
                     mRootActivityContainer.updatePreviousProcess(this);
@@ -2686,6 +3109,34 @@
         }
     }
 
+    void addToStopping(boolean scheduleIdle, boolean idleDelayed, String reason) {
+        if (!mStackSupervisor.mStoppingActivities.contains(this)) {
+            EventLog.writeEvent(EventLogTags.AM_ADD_TO_STOPPING, mUserId,
+                    System.identityHashCode(this), shortComponentName, reason);
+            mStackSupervisor.mStoppingActivities.add(this);
+        }
+
+        final ActivityStack stack = getActivityStack();
+        // If we already have a few activities waiting to stop, then give up on things going idle
+        // and start clearing them out. Or if r is the last of activity of the last task the stack
+        // will be empty and must be cleared immediately.
+        boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE
+                || (isRootOfTask() && stack.getChildCount() <= 1);
+        if (scheduleIdle || forceIdle) {
+            if (DEBUG_PAUSE) {
+                Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle=" + forceIdle
+                        + "immediate=" + !idleDelayed);
+            }
+            if (!idleDelayed) {
+                mStackSupervisor.scheduleIdleLocked();
+            } else {
+                mStackSupervisor.scheduleIdleTimeoutLocked(this);
+            }
+        } else {
+            stack.checkReadyForSleep();
+        }
+    }
+
     void startLaunchTickingLocked() {
         if (Build.IS_USER) {
             return;
@@ -2706,18 +3157,18 @@
             return false;
         }
 
-        Message msg = stack.mHandler.obtainMessage(LAUNCH_TICK_MSG, this);
-        stack.mHandler.removeMessages(LAUNCH_TICK_MSG);
-        stack.mHandler.sendMessageDelayed(msg, LAUNCH_TICK);
+        stack.removeLaunchTickMessages();
+        stack.scheduleLaunchTickForActivity(this);
         return true;
     }
 
     void finishLaunchTickingLocked() {
         launchTickTime = 0;
         final ActivityStack stack = getActivityStack();
-        if (stack != null) {
-            stack.mHandler.removeMessages(LAUNCH_TICK_MSG);
+        if (stack == null) {
+            return;
         }
+        stack.removeLaunchTickMessages();
     }
 
     // IApplicationToken
@@ -3694,7 +4145,7 @@
             if (!attachedToProcess()) {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                         "Config is destroying non-running " + this);
-                stack.destroyActivityLocked(this, true, "config");
+                destroyImmediately(true /* removeFromApp */, "config");
             } else if (mState == PAUSING) {
                 // A little annoying: we are waiting for this activity to finish pausing. Let's not
                 // do anything now, but just flag that it needs to be restarted when done pausing.
@@ -3878,7 +4329,7 @@
         } else {
             final ActivityStack stack = getActivityStack();
             if (stack != null) {
-                stack.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
+                stack.removePauseTimeoutForActivity(this);
             }
             setState(PAUSED, "relaunchActivityLocked");
         }
@@ -3918,7 +4369,7 @@
         if (!visible || mHaveState) {
             // Kill its process immediately because the activity should be in background.
             // The activity state will be update to {@link #DESTROYED} in
-            // {@link ActivityStack#cleanUpActivityLocked} when handling process died.
+            // {@link ActivityStack#cleanUp} when handling process died.
             mAtmService.mH.post(() -> {
                 final WindowProcessController wpc;
                 synchronized (mAtmService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index ba166ea..8bdedff 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -56,10 +56,7 @@
 import static com.android.server.am.ActivityStackProto.TASKS;
 import static com.android.server.wm.ActivityDisplay.POSITION_BOTTOM;
 import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
-import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
@@ -68,14 +65,12 @@
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
 import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
 import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
@@ -89,7 +84,6 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
@@ -123,12 +117,9 @@
 import android.app.WindowConfiguration.WindowingMode;
 import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.DestroyActivityItem;
 import android.app.servertransaction.NewIntentItem;
 import android.app.servertransaction.PauseActivityItem;
 import android.app.servertransaction.ResumeActivityItem;
-import android.app.servertransaction.StopActivityItem;
-import android.app.servertransaction.WindowVisibilityItem;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -163,11 +154,9 @@
 import com.android.server.am.ActivityManagerService.ItemMatcher;
 import com.android.server.am.AppTimeTracker;
 import com.android.server.am.EventLogTags;
-import com.android.server.am.PendingIntentRecord;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -181,7 +170,6 @@
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_APP = TAG + POSTFIX_APP;
     private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
-    private static final String TAG_CONTAINERS = TAG + POSTFIX_CONTAINERS;
     private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
     private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
     private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
@@ -194,7 +182,7 @@
     private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
 
     // Ticks during which we check progress while waiting for an app to launch.
-    static final int LAUNCH_TICK = 500;
+    private static final int LAUNCH_TICK = 500;
 
     // How long we wait until giving up on the last activity to pause.  This
     // is short because it directly impacts the responsiveness of starting the
@@ -222,9 +210,6 @@
     // convertToTranslucent().
     private static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
 
-    // How many activities have to be scheduled to stop to force a stop pass.
-    private static final int MAX_STOPPING_TO_FORCE = 3;
-
     @IntDef(prefix = {"STACK_VISIBILITY"}, value = {
             STACK_VISIBILITY_VISIBLE,
             STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
@@ -411,12 +396,12 @@
     private boolean mTopActivityOccludesKeyguard;
     private ActivityRecord mTopDismissingKeyguardActivity;
 
-    static final int PAUSE_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
-    static final int DESTROY_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 2;
-    static final int LAUNCH_TICK_MSG = FIRST_ACTIVITY_STACK_MSG + 3;
-    static final int STOP_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 4;
-    static final int DESTROY_ACTIVITIES_MSG = FIRST_ACTIVITY_STACK_MSG + 5;
-    static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 6;
+    private static final int PAUSE_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
+    private static final int DESTROY_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 2;
+    private static final int LAUNCH_TICK_MSG = FIRST_ACTIVITY_STACK_MSG + 3;
+    private static final int STOP_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 4;
+    private static final int DESTROY_ACTIVITIES_MSG = FIRST_ACTIVITY_STACK_MSG + 5;
+    private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 6;
 
     // TODO: remove after unification.
     TaskStack mTaskStack;
@@ -430,7 +415,7 @@
         }
     }
 
-    final Handler mHandler;
+    private final Handler mHandler;
 
     private class ActivityStackHandler extends Handler {
 
@@ -467,7 +452,9 @@
                     // so we need to be conservative and assume it isn't.
                     Slog.w(TAG, "Activity destroy timeout for " + r);
                     synchronized (mService.mGlobalLock) {
-                        activityDestroyedLocked(r != null ? r.appToken : null, "destroyTimeout");
+                        if (r != null) {
+                            r.destroyed("destroyTimeout");
+                        }
                     }
                 } break;
                 case STOP_TIMEOUT_MSG: {
@@ -1214,12 +1201,17 @@
         return display != null && display.isSingleTaskInstance();
     }
 
-    final void removeActivitiesFromLRUListLocked(TaskRecord task) {
+    private void removeActivitiesFromLRUList(TaskRecord task) {
         for (ActivityRecord r : task.mActivities) {
             mLRUActivities.remove(r);
         }
     }
 
+    /** @return {@code true} if LRU list contained the specified activity. */
+    final boolean removeActivityFromLRUList(ActivityRecord activity) {
+        return mLRUActivities.remove(activity);
+    }
+
     final boolean updateLRUListLocked(ActivityRecord r) {
         final boolean hadit = mLRUActivities.remove(r);
         mLRUActivities.add(r);
@@ -1616,18 +1608,6 @@
     }
 
     /**
-     * Schedule a pause timeout in case the app doesn't respond. We don't give it much time because
-     * this directly impacts the responsiveness seen by the user.
-     */
-    private void schedulePauseTimeout(ActivityRecord r) {
-        final Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
-        msg.obj = r;
-        r.pauseTime = SystemClock.uptimeMillis();
-        mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
-        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
-    }
-
-    /**
      * Start pausing the currently resumed activity.  It is an error to call this if there
      * is already an activity being paused or there is no resumed activity.
      *
@@ -1727,7 +1707,7 @@
                 return false;
 
             } else {
-                schedulePauseTimeout(prev);
+                schedulePauseTimeoutForActivity(prev);
                 return true;
             }
 
@@ -1807,7 +1787,7 @@
                     prev.setDeferHidingClient(false);
                     // If we were visible then resumeTopActivities will release resources before
                     // stopping.
-                    addToStopping(prev, true /* scheduleIdle */, false /* idleDelayed */,
+                    prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
                             "completePauseLocked");
                 }
             } else {
@@ -1869,32 +1849,6 @@
         mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
     }
 
-    void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed, String reason) {
-        if (!mStackSupervisor.mStoppingActivities.contains(r)) {
-            EventLog.writeEvent(EventLogTags.AM_ADD_TO_STOPPING, r.mUserId,
-                    System.identityHashCode(r), r.shortComponentName, reason);
-            mStackSupervisor.mStoppingActivities.add(r);
-        }
-
-        // If we already have a few activities waiting to stop, then give up
-        // on things going idle and start clearing them out. Or if r is the
-        // last of activity of the last task the stack will be empty and must
-        // be cleared immediately.
-        boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE
-                || (r.isRootOfTask() && mTaskHistory.size() <= 1);
-        if (scheduleIdle || forceIdle) {
-            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle="
-                    + forceIdle + "immediate=" + !idleDelayed);
-            if (!idleDelayed) {
-                mStackSupervisor.scheduleIdleLocked();
-            } else {
-                mStackSupervisor.scheduleIdleTimeoutLocked(r);
-            }
-        } else {
-            checkReadyForSleep();
-        }
-    }
-
     /**
      * Returns true if the stack is translucent and can have other contents visible behind it if
      * needed. A stack is considered translucent if it don't contain a visible or
@@ -2204,7 +2158,7 @@
                                 + " stackShouldBeVisible=" + stackShouldBeVisible
                                 + " behindFullscreenActivity=" + behindFullscreenActivity
                                 + " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
-                        makeInvisible(r);
+                        r.makeInvisible();
                     }
                 }
                 final int windowingMode = getWindowingMode();
@@ -2381,63 +2335,6 @@
         return false;
     }
 
-    // TODO: Should probably be moved into ActivityRecord.
-    private void makeInvisible(ActivityRecord r) {
-        if (!r.visible) {
-            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
-            return;
-        }
-        // Now for any activities that aren't visible to the user, make sure they no longer are
-        // keeping the screen frozen.
-        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.getState());
-        try {
-            final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState(
-                    "makeInvisible", true /* beforeStopping */);
-            // Defer telling the client it is hidden if it can enter Pip and isn't current paused,
-            // stopped or stopping. This gives it a chance to enter Pip in onPause().
-            // TODO: There is still a question surrounding activities in multi-window mode that want
-            // to enter Pip after they are paused, but are still visible. I they should be okay to
-            // enter Pip in those cases, but not "auto-Pip" which is what this condition covers and
-            // the current contract for "auto-Pip" is that the app should enter it before onPause
-            // returns. Just need to confirm this reasoning makes sense.
-            final boolean deferHidingClient = canEnterPictureInPicture
-                    && !r.isState(STOPPING, STOPPED, PAUSED);
-            r.setDeferHidingClient(deferHidingClient);
-            r.setVisible(false);
-
-            switch (r.getState()) {
-                case STOPPING:
-                case STOPPED:
-                    if (r.attachedToProcess()) {
-                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                                "Scheduling invisibility: " + r);
-                        mService.getLifecycleManager().scheduleTransaction(r.app.getThread(),
-                                r.appToken, WindowVisibilityItem.obtain(false /* showWindow */));
-                    }
-
-                    // Reset the flag indicating that an app can enter picture-in-picture once the
-                    // activity is hidden
-                    r.supportsEnterPipOnTaskSwitch = false;
-                    break;
-
-                case INITIALIZING:
-                case RESUMED:
-                case PAUSING:
-                case PAUSED:
-                case STARTED:
-                    addToStopping(r, true /* scheduleIdle */,
-                            canEnterPictureInPicture /* idleDelayed */, "makeInvisible");
-                    break;
-
-                default:
-                    break;
-            }
-        } catch (Exception e) {
-            // Just skip on any failure; we'll make it visible when it next restarts.
-            Slog.w(TAG, "Exception thrown making hidden: " + r.intent.getComponent(), e);
-        }
-    }
-
     private boolean updateBehindFullscreen(boolean stackInvisible, boolean behindFullscreenActivity,
             ActivityRecord r) {
         if (r.fullscreen) {
@@ -3655,49 +3552,6 @@
         return taskTop;
     }
 
-    void sendActivityResultLocked(int callingUid, ActivityRecord r,
-            String resultWho, int requestCode, int resultCode, Intent data) {
-
-        if (callingUid > 0) {
-            mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName,
-                    data, r.getUriPermissionsLocked(), r.mUserId);
-        }
-
-        if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
-                + " : who=" + resultWho + " req=" + requestCode
-                + " res=" + resultCode + " data=" + data);
-        if (mResumedActivity == r && r.attachedToProcess()) {
-            try {
-                ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
-                list.add(new ResultInfo(resultWho, requestCode,
-                        resultCode, data));
-                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
-                        ActivityResultItem.obtain(list));
-                return;
-            } catch (Exception e) {
-                Slog.w(TAG, "Exception thrown sending result to " + r, e);
-            }
-        }
-
-        r.addResultLocked(null, resultWho, requestCode, resultCode, data);
-    }
-
-    /** Returns true if the task is one of the task finishing on-top of the top running task. */
-    private boolean isATopFinishingTask(TaskRecord task) {
-        for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
-            final TaskRecord current = mTaskHistory.get(i);
-            final ActivityRecord r = current.topRunningActivityLocked();
-            if (r != null) {
-                // We got a top running activity, so there isn't a top finishing task...
-                return false;
-            }
-            if (current == task) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void adjustFocusedActivityStack(ActivityRecord r, String reason) {
         if (!mRootActivityContainer.isTopDisplayFocusedStack(this) ||
                 ((mResumedActivity != r) && (mResumedActivity != null))) {
@@ -3776,64 +3630,6 @@
         return stack;
     }
 
-    final void stopActivityLocked(ActivityRecord r) {
-        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + r);
-        if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
-                || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
-            if (!r.finishing) {
-                if (!shouldSleepActivities()) {
-                    if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + r);
-                    if (r.finishIfPossible("stop-no-history", false /* oomAdj */)
-                            != FINISH_RESULT_CANCELLED) {
-                        // {@link adjustFocusedActivityStack} must have been already called.
-                        r.resumeKeyDispatchingLocked();
-                        return;
-                    }
-                } else {
-                    if (DEBUG_STATES) Slog.d(TAG_STATES, "Not finishing noHistory " + r
-                            + " on stop because we're just sleeping");
-                }
-            }
-        }
-
-        if (r.attachedToProcess()) {
-            adjustFocusedActivityStack(r, "stopActivity");
-            r.resumeKeyDispatchingLocked();
-            try {
-                r.stopped = false;
-                if (DEBUG_STATES) Slog.v(TAG_STATES,
-                        "Moving to STOPPING: " + r + " (stop requested)");
-                r.setState(STOPPING, "stopActivityLocked");
-                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
-                        "Stopping visible=" + r.visible + " for " + r);
-                if (!r.visible) {
-                    r.setVisible(false);
-                }
-                EventLogTags.writeAmStopActivity(
-                        r.mUserId, System.identityHashCode(r), r.shortComponentName);
-                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
-                        StopActivityItem.obtain(r.visible, r.configChangeFlags));
-                if (shouldSleepOrShutDownActivities()) {
-                    r.setSleeping(true);
-                }
-                Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
-                mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);
-            } catch (Exception e) {
-                // Maybe just ignore exceptions here...  if the process
-                // has crashed, our death notification will clean things
-                // up.
-                Slog.w(TAG, "Exception thrown during pause", e);
-                // Just in case, assume it to be stopped.
-                r.stopped = true;
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r);
-                r.setState(STOPPED, "stopActivityLocked");
-                if (r.deferRelaunchUntilPaused) {
-                    destroyActivityLocked(r, true, "stop-except");
-                }
-            }
-        }
-    }
-
     /** Finish all activities that were started for result from the specified activity. */
     final void finishSubActivityLocked(ActivityRecord self, String resultWho, int requestCode) {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -4103,7 +3899,7 @@
      * an activity moves away from the stack.
      */
     void onActivityRemovedFromStack(ActivityRecord r) {
-        removeTimeoutsForActivityLocked(r);
+        removeTimeoutsForActivity(r);
 
         if (mResumedActivity != null && mResumedActivity == r) {
             setResumedActivity(null, "onActivityRemovedFromStack");
@@ -4119,132 +3915,64 @@
         }
     }
 
-    /**
-     * Perform the common clean-up of an activity record.  This is called both
-     * as part of destroyActivityLocked() (when destroying the client-side
-     * representation) and cleaning things up as a result of its hosting
-     * processing going away, in which case there is no remaining client-side
-     * state to destroy so only the cleanup here is needed.
-     *
-     * Note: Call before #removeActivityFromHistoryLocked.
-     */
-    private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean setState) {
-        onActivityRemovedFromStack(r);
-
-        r.deferRelaunchUntilPaused = false;
-        r.frozenBeforeDestroy = false;
-
-        if (setState) {
-            if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (cleaning up)");
-            r.setState(DESTROYED, "cleanupActivityLocked");
-            if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + r);
-            r.app = null;
-        }
-
-        // Inform supervisor the activity has been removed.
-        mStackSupervisor.cleanupActivity(r);
-
-
-        // Remove any pending results.
-        if (r.finishing && r.pendingResults != null) {
-            for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
-                PendingIntentRecord rec = apr.get();
-                if (rec != null) {
-                    mService.mPendingIntentController.cancelIntentSender(rec, false);
-                }
-            }
-            r.pendingResults = null;
-        }
-
-        if (cleanServices) {
-            cleanUpActivityServicesLocked(r);
-        }
-
-        // Get rid of any pending idle timeouts.
-        removeTimeoutsForActivityLocked(r);
-        // Clean-up activities are no longer relaunching (e.g. app process died). Notify window
-        // manager so it can update its bookkeeping.
-        mWindowManager.notifyAppRelaunchesCleared(r.appToken);
-    }
-
-    private void removeTimeoutsForActivityLocked(ActivityRecord r) {
+    /// HANDLER INTERFACE BEGIN
+    void removeTimeoutsForActivity(ActivityRecord r) {
         mStackSupervisor.removeTimeoutsForActivityLocked(r);
-        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
-        mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
-        mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
+        removePauseTimeoutForActivity(r);
+        removeStopTimeoutForActivity(r);
+        removeDestroyTimeoutForActivity(r);
         r.finishLaunchTickingLocked();
     }
 
-    private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) {
-        r.finishActivityResults(Activity.RESULT_CANCELED, null /* resultData */);
-        r.makeFinishingLocked();
-        if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
-                "Removing activity " + r + " from stack callers=" + Debug.getCallers(5));
-
-        r.takeFromHistory();
-        removeTimeoutsForActivityLocked(r);
-        if (DEBUG_STATES) Slog.v(TAG_STATES,
-                "Moving to DESTROYED: " + r + " (removed from history)");
-        r.setState(DESTROYED, "removeActivityFromHistoryLocked");
-        if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + r);
-        r.app = null;
-        r.removeWindowContainer();
-        final TaskRecord task = r.getTaskRecord();
-        final boolean lastActivity = task != null ? task.removeActivity(r) : false;
-        // If we are removing the last activity in the task, not including task overlay activities,
-        // then fall through into the block below to remove the entire task itself
-        final boolean onlyHasTaskOverlays = task != null
-                ? task.onlyHasTaskOverlayActivities(false /* excludingFinishing */) : false;
-
-        if (lastActivity || onlyHasTaskOverlays) {
-            if (DEBUG_STACK) {
-                Slog.i(TAG_STACK,
-                        "removeActivityFromHistoryLocked: last activity removed from " + this
-                                + " onlyHasTaskOverlays=" + onlyHasTaskOverlays);
-            }
-
-            // The following block can be executed multiple times if there is more than one overlay.
-            // {@link ActivityStackSupervisor#removeTaskByIdLocked} handles this by reverse lookup
-            // of the task by id and exiting early if not found.
-            if (onlyHasTaskOverlays) {
-                // When destroying a task, tell the supervisor to remove it so that any activity it
-                // has can be cleaned up correctly. This is currently the only place where we remove
-                // a task with the DESTROYING mode, so instead of passing the onlyHasTaskOverlays
-                // state into removeTask(), we just clear the task here before the other residual
-                // work.
-                // TODO: If the callers to removeTask() changes such that we have multiple places
-                //       where we are destroying the task, move this back into removeTask()
-                mStackSupervisor.removeTaskByIdLocked(task.taskId, false /* killProcess */,
-                        !REMOVE_FROM_RECENTS, PAUSE_IMMEDIATELY, reason);
-            }
-
-            // We must keep the task around until all activities are destroyed. The following
-            // statement will only execute once since overlays are also considered activities.
-            if (lastActivity) {
-                removeTask(task, reason, REMOVE_TASK_MODE_DESTROYING);
-            }
-        }
-        cleanUpActivityServicesLocked(r);
-        r.removeUriPermissionsLocked();
-    }
-
-    /**
-     * Perform clean-up of service connections in an activity record.
-     */
-    private void cleanUpActivityServicesLocked(ActivityRecord r) {
-        if (r.mServiceConnectionsHolder == null) {
-            return;
-        }
-        // Throw away any services that have been bound by this activity.
-        r.mServiceConnectionsHolder.disconnectActivityFromServices();
-    }
-
-    final void scheduleDestroyActivities(WindowProcessController owner, String reason) {
-        Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
+    void scheduleDestroyActivities(WindowProcessController owner, String reason) {
+        final Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
         msg.obj = new ScheduleDestroyArgs(owner, reason);
         mHandler.sendMessage(msg);
     }
 
+    void scheduleDestroyTimeoutForActivity(ActivityRecord r) {
+        final Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
+        mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
+    }
+
+    void removeDestroyTimeoutForActivity(ActivityRecord r) {
+        mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
+    }
+
+    void scheduleStopTimeoutForActivity(ActivityRecord r) {
+        final Message msg = mHandler.obtainMessage(STOP_TIMEOUT_MSG, r);
+        mHandler.sendMessageDelayed(msg, STOP_TIMEOUT);
+    }
+
+    void removeStopTimeoutForActivity(ActivityRecord r) {
+        mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
+    }
+
+    /**
+     * Schedule a pause timeout in case the app doesn't respond. We don't give it much time because
+     * this directly impacts the responsiveness seen by the user.
+     */
+    private void schedulePauseTimeoutForActivity(ActivityRecord r) {
+        final Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG, r);
+        r.pauseTime = SystemClock.uptimeMillis();
+        mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
+        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
+    }
+
+    void removePauseTimeoutForActivity(ActivityRecord r) {
+        mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+    }
+
+    void scheduleLaunchTickForActivity(ActivityRecord r) {
+        final Message msg = mHandler.obtainMessage(LAUNCH_TICK_MSG, r);
+        mHandler.sendMessageDelayed(msg, LAUNCH_TICK);
+    }
+
+    void removeLaunchTickMessages() {
+        mHandler.removeMessages(LAUNCH_TICK_MSG);
+    }
+    /// HANDLER INTERFACE END
+
     private void destroyActivitiesLocked(WindowProcessController owner, String reason) {
         boolean lastIsOpaque = false;
         boolean activityRemoved = false;
@@ -4269,7 +3997,7 @@
                             + " in state " + r.getState()
                             + " resumed=" + mResumedActivity
                             + " pausing=" + mPausingActivity + " for reason " + reason);
-                    if (destroyActivityLocked(r, true, reason)) {
+                    if (r.destroyImmediately(true /* removeFromTask */, reason)) {
                         activityRemoved = true;
                     }
                 }
@@ -4280,16 +4008,6 @@
         }
     }
 
-    final boolean safelyDestroyActivityLocked(ActivityRecord r, String reason) {
-        if (r.isDestroyable()) {
-            if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
-                    "Destroying " + r + " in state " + r.getState() + " resumed=" + mResumedActivity
-                    + " pausing=" + mPausingActivity + " for reason " + reason);
-            return destroyActivityLocked(r, true, reason);
-        }
-        return false;
-    }
-
     final int releaseSomeActivitiesLocked(WindowProcessController app, ArraySet<TaskRecord> tasks,
             String reason) {
         // Iterate over tasks starting at the back (oldest) first.
@@ -4313,7 +4031,7 @@
                     if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + activity
                             + " in state " + activity.getState() + " resumed=" + mResumedActivity
                             + " pausing=" + mPausingActivity + " for reason " + reason);
-                    destroyActivityLocked(activity, true, reason);
+                    activity.destroyImmediately(true /* removeFromApp */, reason);
                     if (activities.get(actNdx) != activity) {
                         // Was removed from list, back up so we don't miss the next one.
                         actNdx--;
@@ -4335,145 +4053,6 @@
         return numReleased;
     }
 
-    /**
-     * Destroy the current CLIENT SIDE instance of an activity.  This may be * called both when
-     * actually finishing an activity, or when performing a configuration switch where we destroy
-     * the current client-side object but then create a new client-side object for this same
-     * HistoryRecord.
-     * Normally the server-side record will be removed when the client reports back after
-     * destruction. If, however, at this point there is no client process attached, the record will
-     * removed immediately.
-     */
-    final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
-        if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
-                "Removing activity from " + reason + ": token=" + r
-                        + ", app=" + (r.hasProcess() ? r.app.mName : "(null)"));
-
-        if (r.isState(DESTROYING, DESTROYED)) {
-            if (DEBUG_STATES) Slog.v(TAG_STATES, "activity " + r + " already destroying."
-                    + "skipping request with reason:" + reason);
-            return false;
-        }
-
-        EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
-                r.mUserId, System.identityHashCode(r),
-                r.getTaskRecord().taskId, r.shortComponentName, reason);
-
-        boolean removedFromHistory = false;
-
-        cleanUpActivityLocked(r, false, false);
-
-        final boolean hadApp = r.hasProcess();
-
-        if (hadApp) {
-            if (removeFromApp) {
-                r.app.removeActivity(r);
-                if (!r.app.hasActivities()) {
-                    mService.clearHeavyWeightProcessIfEquals(r.app);
-                }
-                if (!r.app.hasActivities()) {
-                    // Update any services we are bound to that might care about whether
-                    // their client may have activities.
-                    // No longer have activities, so update LRU list and oom adj.
-                    r.app.updateProcessInfo(true /* updateServiceConnectionActivities */,
-                            false /* activityChange */, true /* updateOomAdj */);
-                }
-            }
-
-            boolean skipDestroy = false;
-
-            try {
-                if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
-                mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
-                        DestroyActivityItem.obtain(r.finishing, r.configChangeFlags));
-            } catch (Exception e) {
-                // We can just ignore exceptions here...  if the process
-                // has crashed, our death notification will clean things
-                // up.
-                //Slog.w(TAG, "Exception thrown during finish", e);
-                if (r.finishing) {
-                    removeActivityFromHistoryLocked(r, reason + " exceptionInScheduleDestroy");
-                    removedFromHistory = true;
-                    skipDestroy = true;
-                }
-            }
-
-            r.nowVisible = false;
-
-            // If the activity is finishing, we need to wait on removing it
-            // from the list to give it a chance to do its cleanup.  During
-            // that time it may make calls back with its token so we need to
-            // be able to find it on the list and so we don't want to remove
-            // it from the list yet.  Otherwise, we can just immediately put
-            // it in the destroyed state since we are not removing it from the
-            // list.
-            if (r.finishing && !skipDestroy) {
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYING: " + r
-                        + " (destroy requested)");
-                r.setState(DESTROYING,
-                        "destroyActivityLocked. finishing and not skipping destroy");
-                Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
-                mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
-            } else {
-                if (DEBUG_STATES) Slog.v(TAG_STATES,
-                        "Moving to DESTROYED: " + r + " (destroy skipped)");
-                r.setState(DESTROYED,
-                        "destroyActivityLocked. not finishing or skipping destroy");
-                if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + r);
-                r.app = null;
-            }
-        } else {
-            // remove this record from the history.
-            if (r.finishing) {
-                removeActivityFromHistoryLocked(r, reason + " hadNoApp");
-                removedFromHistory = true;
-            } else {
-                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (no app)");
-                r.setState(DESTROYED, "destroyActivityLocked. not finishing and had no app");
-                if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during destroy for activity " + r);
-                r.app = null;
-            }
-        }
-
-        r.configChangeFlags = 0;
-
-        if (!mLRUActivities.remove(r) && hadApp) {
-            Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list");
-        }
-
-        return removedFromHistory;
-    }
-
-    final void activityDestroyedLocked(IBinder token, String reason) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            activityDestroyedLocked(ActivityRecord.forTokenLocked(token), reason);
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    /**
-     * This method is to only be called from the client via binder when the activity is destroyed
-     * AND finished.
-     */
-    final void activityDestroyedLocked(ActivityRecord record, String reason) {
-        if (record != null) {
-            mHandler.removeMessages(DESTROY_TIMEOUT_MSG, record);
-        }
-
-        if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + record);
-
-        if (isInStackLocked(record) != null) {
-            if (record.isState(DESTROYING, DESTROYED)) {
-                cleanUpActivityLocked(record, true, false);
-                removeActivityFromHistoryLocked(record, reason);
-            }
-        }
-
-        mRootActivityContainer.resumeFocusedStacksTopActivities();
-    }
-
     private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
             WindowProcessController app, String listName) {
         int i = list.size();
@@ -4486,7 +4065,7 @@
             if (r.app == app) {
                 if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "---> REMOVING this entry!");
                 list.remove(i);
-                removeTimeoutsForActivityLocked(r);
+                removeTimeoutsForActivity(r);
             }
         }
     }
@@ -4581,9 +4160,9 @@
                         // other apps when user transfers focus to the restarted activity.
                         r.nowVisible = r.visible;
                     }
-                    cleanUpActivityLocked(r, true, true);
+                    r.cleanUp(true /* cleanServices */, true /* setState */);
                     if (remove) {
-                        removeActivityFromHistoryLocked(r, "appDied");
+                        r.removeFromHistory("appDied");
                     }
                 }
             }
@@ -4774,16 +4353,6 @@
         return true;
     }
 
-    static void logStartActivity(int tag, ActivityRecord r, TaskRecord task) {
-        final Uri data = r.intent.getData();
-        final String strData = data != null ? data.toSafeString() : null;
-
-        EventLog.writeEvent(tag,
-                r.mUserId, System.identityHashCode(r), task.taskId,
-                r.shortComponentName, r.intent.getAction(),
-                r.intent.getType(), strData, r.intent.getFlags());
-    }
-
     /**
      * Ensures all visible activities at or below the input activity have the right configuration.
      */
@@ -5192,7 +4761,7 @@
             EventLog.writeEvent(EventLogTags.AM_REMOVE_TASK, task.taskId, getStackId());
         }
 
-        removeActivitiesFromLRUListLocked(task);
+        removeActivitiesFromLRUList(task);
         updateTaskMovement(task, true);
 
         if (mode == REMOVE_TASK_MODE_DESTROYING) {
@@ -5375,7 +4944,7 @@
         // If the activity was previously pausing, then ensure we transfer that as well
         if (setPause) {
             mPausingActivity = r;
-            schedulePauseTimeout(r);
+            schedulePauseTimeoutForActivity(r);
         }
         // Move the stack in which we are placing the activity to the front.
         moveToFront(reason);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index d0c70de..a06b9ce 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -42,6 +42,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.graphics.Rect.copyOrNull;
 import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
+import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -1028,9 +1029,8 @@
         if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION
                 || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
             if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(-1,
-                        resultRecord, resultWho, requestCode,
-                        Activity.RESULT_CANCELED, null);
+                resultRecord.sendResult(INVALID_UID, resultWho, requestCode,
+                        Activity.RESULT_CANCELED, null /* data */);
             }
             final String msg;
             if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
@@ -1349,7 +1349,7 @@
                     // TODO(b/137329632): Wait for idle of the right activity, not just any.
                     r.destroyIfPossible("activityIdleInternalLocked");
                 } else {
-                    stack.stopActivityLocked(r);
+                    r.stopIfPossible();
                 }
             }
         }
@@ -1360,7 +1360,7 @@
             r = finishes.get(i);
             final ActivityStack stack = r.getActivityStack();
             if (stack != null) {
-                activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
+                activityRemoved |= r.destroyImmediately(true /* removeFromApp */, "finish-idle");
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d6250f6..48bc963 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -55,6 +55,7 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.INVALID_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -752,8 +753,8 @@
 
         if (err != START_SUCCESS) {
             if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(
-                        -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);
+                resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
+                        null /* data */);
             }
             SafeActivityOptions.abort(options);
             return err;
@@ -817,8 +818,8 @@
 
         if (abort) {
             if (resultRecord != null) {
-                resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
-                        RESULT_CANCELED, null);
+                resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
+                        null /* data */);
             }
             // We pretend to the caller that it was really started, but
             // they will just get a cancel result.
@@ -1450,18 +1451,17 @@
      * TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere.
      */
     private boolean handleBackgroundActivityAbort(ActivityRecord r) {
-        // TODO(b/131747138): Remove toast and refactor related code in Q release.
-        boolean abort = !mService.isBackgroundActivityStartsEnabled();
+        // TODO(b/131747138): Remove toast and refactor related code in R release.
+        final boolean abort = !mService.isBackgroundActivityStartsEnabled();
         if (!abort) {
             return false;
         }
-        ActivityRecord resultRecord = r.resultTo;
-        String resultWho = r.resultWho;
+        final ActivityRecord resultRecord = r.resultTo;
+        final String resultWho = r.resultWho;
         int requestCode = r.requestCode;
         if (resultRecord != null) {
-            ActivityStack resultStack = resultRecord.getActivityStack();
-            resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
-                    RESULT_CANCELED, null);
+            resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
+                    null /* data */);
         }
         // We pretend to the caller that it was really started to make it backward compatible, but
         // they will just get a cancel result.
@@ -1626,12 +1626,9 @@
         }
 
         if (mStartActivity.packageName == null) {
-            final ActivityStack sourceStack = mStartActivity.resultTo != null
-                    ? mStartActivity.resultTo.getActivityStack() : null;
-            if (sourceStack != null) {
-                sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
-                        mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
-                        null /* data */);
+            if (mStartActivity.resultTo != null) {
+                mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho,
+                        mStartActivity.requestCode, RESULT_CANCELED, null /* data */);
             }
             ActivityOptions.abort(mOptions);
             return START_CLASS_NOT_FOUND;
@@ -1708,8 +1705,8 @@
             EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.mUserId,
                     mStartActivity.getTaskRecord().taskId);
         }
-        ActivityStack.logStartActivity(
-                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTaskRecord());
+        mStartActivity.logStartActivity(
+                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity.getTaskRecord());
         mTargetStack.mLastPausedActivity = null;
 
         mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
@@ -1927,17 +1924,14 @@
     }
 
     private void sendNewTaskResultRequestIfNeeded() {
-        final ActivityStack sourceStack = mStartActivity.resultTo != null
-                ? mStartActivity.resultTo.getActivityStack() : null;
-        if (sourceStack != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+        if (mStartActivity.resultTo != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
             // For whatever reason this activity is being launched into a new task...
             // yet the caller has requested a result back.  Well, that is pretty messed up,
             // so instead immediately send back a cancel and let the new task continue launched
             // as normal without a dependency on its originator.
             Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
-            sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
-                    mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
-                    null /* data */);
+            mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho,
+                    mStartActivity.requestCode, RESULT_CANCELED, null /* data */);
             mStartActivity.resultTo = null;
         }
     }
@@ -2362,7 +2356,7 @@
             return;
         }
 
-        ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTaskRecord());
+        activity.logStartActivity(AM_NEW_INTENT, activity.getTaskRecord());
         activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
                 mStartActivity.launchedFromPackage);
         mIntentDelivered = true;
@@ -2429,7 +2423,7 @@
             ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags);
             mKeepCurTransition = true;
             if (top != null) {
-                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTaskRecord());
+                mStartActivity.logStartActivity(AM_NEW_INTENT, top.getTaskRecord());
                 deliverNewIntent(top);
                 // For paranoia, make sure we have correctly resumed the top activity.
                 mTargetStack.mLastPausedActivity = null;
@@ -2448,7 +2442,7 @@
                 final TaskRecord task = top.getTaskRecord();
                 task.moveActivityToFrontLocked(top);
                 top.updateOptionsLocked(mOptions);
-                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
+                mStartActivity.logStartActivity(AM_NEW_INTENT, task);
                 deliverNewIntent(top);
                 mTargetStack.mLastPausedActivity = null;
                 if (mDoResume) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 2da2ebc..3c5947a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -46,7 +46,6 @@
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
-import static android.os.Build.VERSION_CODES.N;
 import static android.os.FactoryTest.FACTORY_TEST_HIGH_LEVEL;
 import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
 import static android.os.FactoryTest.FACTORY_TEST_OFF;
@@ -1754,9 +1753,14 @@
     public final void activityDestroyed(IBinder token) {
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
         synchronized (mGlobalLock) {
-            ActivityStack stack = ActivityRecord.getStackLocked(token);
-            if (stack != null) {
-                stack.activityDestroyedLocked(token, "activityDestroyed");
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                final ActivityRecord activity = ActivityRecord.forTokenLocked(token);
+                if (activity != null) {
+                    activity.destroyed("activityDestroyed");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
             }
         }
     }
@@ -3237,7 +3241,7 @@
                 if (r == null) {
                     return false;
                 }
-                return r.getActivityStack().safelyDestroyActivityLocked(r, "app-req");
+                return r.safelyDestroy("app-req");
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -6485,8 +6489,7 @@
             synchronized (mGlobalLock) {
                 final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
                 if (r != null && r.getActivityStack() != null) {
-                    r.getActivityStack().sendActivityResultLocked(callingUid, r, resultWho,
-                            requestCode, resultCode, data);
+                    r.sendResult(callingUid, resultWho, requestCode, resultCode, data);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4135ef3..e851a06 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -796,10 +796,14 @@
                 mTmpApplySurfaceChangesTransactionState.obscured = true;
             }
 
-            mTmpApplySurfaceChangesTransactionState.displayHasContent |=
-                    root.handleNotObscuredLocked(w,
-                            mTmpApplySurfaceChangesTransactionState.obscured,
-                            mTmpApplySurfaceChangesTransactionState.syswin);
+            final boolean displayHasContent = root.handleNotObscuredLocked(w,
+                    mTmpApplySurfaceChangesTransactionState.obscured,
+                    mTmpApplySurfaceChangesTransactionState.syswin);
+
+            if (!mTmpApplySurfaceChangesTransactionState.displayHasContent
+                    && !getDisplayPolicy().isWindowExcludedFromContent(w)) {
+                mTmpApplySurfaceChangesTransactionState.displayHasContent |= displayHasContent;
+            }
 
             if (w.mHasSurface && isDisplayed) {
                 final int type = w.mAttrs.type;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index e35ef25..e5962ae 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -3790,6 +3790,20 @@
         mPointerLocationView = null;
     }
 
+    /**
+     * Check if the window could be excluded from checking if the display has content.
+     *
+     * @param w WindowState to check if should be excluded.
+     * @return True if the window type is PointerLocation which is excluded.
+     */
+    boolean isWindowExcludedFromContent(WindowState w) {
+        if (w != null && mPointerLocationView != null) {
+            return w.mClient == mPointerLocationView.getWindowToken();
+        }
+
+        return false;
+    }
+
     @VisibleForTesting
     static boolean isOverlappingWithNavBar(WindowState targetWindow, WindowState navBarWindow) {
         if (navBarWindow == null || !navBarWindow.isVisibleLw()
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index caa8363..1a8944a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -149,8 +149,8 @@
             // traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more
             // things (e.g. the measure can be done earlier). The actual stop will be performed when
             // it reports idle.
-            targetStack.addToStopping(targetActivity, true /* scheduleIdle */,
-                    true /* idleDelayed */, "preloadRecents");
+            targetActivity.addToStopping(true /* scheduleIdle */, true /* idleDelayed */,
+                    "preloadRecents");
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index f51d4c4..1c015d0 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -159,11 +159,23 @@
         }
 
         // STEP 2: Resolve launch windowing mode.
-        // STEP 2.1: Determine if any parameter has specified initial bounds. That might be the
-        // launch bounds from activity options, or size/gravity passed in layout. It also treats the
-        // launch windowing mode in options as a suggestion for future resolution.
+        // STEP 2.1: Determine if any parameter can specify initial bounds/windowing mode. That
+        // might be the launch bounds from activity options, or size/gravity passed in layout. It
+        // also treats the launch windowing mode in options and source activity windowing mode in
+        // some cases as a suggestion for future resolution.
         int launchMode = options != null ? options.getLaunchWindowingMode()
                 : WINDOWING_MODE_UNDEFINED;
+        // In some cases we want to use the source's windowing mode as the default value, e.g. when
+        // source is a freeform window in a fullscreen display launching an activity on the same
+        // display.
+        if (launchMode == WINDOWING_MODE_UNDEFINED
+                && canInheritWindowingModeFromSource(display, source)) {
+            launchMode = source.getWindowingMode();
+            if (DEBUG) {
+                appendLog("inherit-from-source="
+                        + WindowConfiguration.windowingModeToString(launchMode));
+            }
+        }
         // hasInitialBounds is set if either activity options or layout has specified bounds. If
         // that's set we'll skip some adjustments later to avoid overriding the initial bounds.
         boolean hasInitialBounds = false;
@@ -342,6 +354,31 @@
                 ? displayId : DEFAULT_DISPLAY;
     }
 
+    private boolean canInheritWindowingModeFromSource(@NonNull ActivityDisplay display,
+            @Nullable ActivityRecord source) {
+        if (source == null) {
+            return false;
+        }
+
+        // There is not really any strong reason to tie the launching windowing mode and the source
+        // on freeform displays. The launching windowing mode is more tied to the content of the new
+        // activities.
+        if (display.inFreeformWindowingMode()) {
+            return false;
+        }
+
+        final int sourceWindowingMode = source.getWindowingMode();
+        if (sourceWindowingMode != WINDOWING_MODE_FULLSCREEN
+                && sourceWindowingMode != WINDOWING_MODE_FREEFORM) {
+            return false;
+        }
+
+        // Only inherit windowing mode if both source and target activities are on the same display.
+        // Otherwise we may have unintended freeform windows showing up if an activity in freeform
+        // window launches an activity on a fullscreen display by specifying display ID.
+        return display.mDisplayId == source.getDisplayId();
+    }
+
     private boolean canApplyFreeformWindowPolicy(@NonNull ActivityDisplay display, int launchMode) {
         return mSupervisor.mService.mSupportsFreeformWindowManagement
                 && (display.inFreeformWindowingMode() || launchMode == WINDOWING_MODE_FREEFORM);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 05cfbd4..750926f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -52,9 +52,12 @@
          * Called when the windows for accessibility changed.
          *
          * @param forceSend Send the windows for accessibility even if they haven't changed.
+         * @param topFocusedDisplayId The display Id which has the top focused window.
+         * @param topFocusedWindowToken The window token of top focused window.
          * @param windows The windows for accessibility.
          */
-        void onWindowsForAccessibilityChanged(boolean forceSend, List<WindowInfo> windows);
+        void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId,
+                IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows);
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 939aafa..356423a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -76,6 +76,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.testing.DexmakerShareClassLoaderRule;
+import android.view.Display;
 import android.view.KeyEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
@@ -178,7 +179,8 @@
         // Fake a11yWindowInfo and remote a11y connection for tests.
         addA11yWindowInfo(mA11yWindowInfos, WINDOWID, false);
         addA11yWindowInfo(mA11yWindowInfos, PIP_WINDOWID, true);
-        when(mMockA11yWindowManager.getWindowListLocked()).thenReturn(mA11yWindowInfos);
+        when(mMockA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY))
+                .thenReturn(mA11yWindowInfos);
         when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID))
                 .thenReturn(mA11yWindowInfos.get(0));
         when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(PIP_WINDOWID))
@@ -289,8 +291,9 @@
 
     @Test
     public void getWindows_notTrackingWindows_invokeOnClientChange() {
-        when(mMockA11yWindowManager.getWindowListLocked()).thenReturn(null);
-        when(mMockA11yWindowManager.isTrackingWindowsLocked()).thenReturn(false);
+        when(mMockA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)).thenReturn(null);
+        when(mMockA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY))
+                .thenReturn(false);
 
         mServiceConnection.getWindows();
         verify(mMockSystemSupport).onClientChangeLocked(false);
@@ -315,8 +318,9 @@
 
     @Test
     public void getWindow_notTrackingWindows_invokeOnClientChange() {
-        when(mMockA11yWindowManager.getWindowListLocked()).thenReturn(null);
-        when(mMockA11yWindowManager.isTrackingWindowsLocked()).thenReturn(false);
+        when(mMockA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)).thenReturn(null);
+        when(mMockA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY))
+                .thenReturn(false);
 
         mServiceConnection.getWindow(WINDOWID);
         verify(mMockSystemSupport).onClientChangeLocked(false);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 93c16fe..b7b5a4e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -79,17 +79,22 @@
     private static final int USER_SYSTEM_ID = UserHandle.USER_SYSTEM;
     private static final int USER_PROFILE = 11;
     private static final int USER_PROFILE_PARENT = 1;
-    // TO-DO [Multi-Display] : change the display count to 2
-    private static final int DISPLAY_COUNT = 1;
+    private static final int SECONDARY_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1;
     private static final int NUM_GLOBAL_WINDOWS = 4;
     private static final int NUM_APP_WINDOWS = 4;
-    private static final int NUM_OF_WINDOWS = (NUM_GLOBAL_WINDOWS + NUM_APP_WINDOWS)
-            * DISPLAY_COUNT;
+    private static final int NUM_OF_WINDOWS = (NUM_GLOBAL_WINDOWS + NUM_APP_WINDOWS);
     private static final int DEFAULT_FOCUSED_INDEX = 1;
     private static final int SCREEN_WIDTH = 1080;
     private static final int SCREEN_HEIGHT = 1920;
 
     private AccessibilityWindowManager mA11yWindowManager;
+    // Window manager will support multiple focused window if config_perDisplayFocusEnabled is true,
+    // i.e., each display would have its current focused window, and one of all focused windows
+    // would be top focused window. Otherwise, window manager only supports one focused window
+    // at all displays, and that focused window would be top focused window.
+    private boolean mSupportPerDisplayFocus = false;
+    private int mTopFocusedDisplayId = Display.INVALID_DISPLAY;
+    private IBinder mTopFocusedWindowToken = null;
 
     // List of window token, mapping from windowId -> window token.
     private final SparseArray<IWindow> mA11yWindowTokens = new SparseArray<>();
@@ -123,12 +128,9 @@
                 mMockA11yEventSender,
                 mMockA11ySecurityPolicy,
                 mMockA11yUserManager);
-
-        for (int i = 0; i < DISPLAY_COUNT; i++) {
-            when(mMockWindowManagerInternal.setWindowsForAccessibilityCallback(eq(i), any()))
-                    .thenReturn(true);
-            startTrackingPerDisplay(i);
-        }
+        // Starts tracking window of default display and sets the default display
+        // as top focused display before each testing starts.
+        startTrackingPerDisplay(Display.DEFAULT_DISPLAY);
 
         // AccessibilityEventSender is invoked during onWindowsForAccessibilityChanged.
         // Resets it for mockito verify of further test case.
@@ -143,7 +145,7 @@
     @Test
     public void startTrackingWindows_shouldEnableWindowManagerCallback() {
         // AccessibilityWindowManager#startTrackingWindows already invoked in setup.
-        assertTrue(mA11yWindowManager.isTrackingWindowsLocked());
+        assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY));
         final WindowsForAccessibilityCallback callbacks =
                 mCallbackOfWindows.get(Display.DEFAULT_DISPLAY);
         verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback(
@@ -152,11 +154,11 @@
 
     @Test
     public void stopTrackingWindows_shouldDisableWindowManagerCallback() {
-        assertTrue(mA11yWindowManager.isTrackingWindowsLocked());
+        assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY));
         Mockito.reset(mMockWindowManagerInternal);
 
-        mA11yWindowManager.stopTrackingWindows();
-        assertFalse(mA11yWindowManager.isTrackingWindowsLocked());
+        mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY);
+        assertFalse(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY));
         verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback(
                 eq(Display.DEFAULT_DISPLAY), isNull());
 
@@ -164,11 +166,11 @@
 
     @Test
     public void stopTrackingWindows_shouldClearWindows() {
-        assertTrue(mA11yWindowManager.isTrackingWindowsLocked());
+        assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY));
         final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
 
-        mA11yWindowManager.stopTrackingWindows();
-        assertNull(mA11yWindowManager.getWindowListLocked());
+        mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY);
+        assertNull(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY));
         assertEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT),
                 AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
         assertEquals(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID),
@@ -176,6 +178,20 @@
     }
 
     @Test
+    public void stopTrackingWindows_onNonTopFocusedDisplay_shouldNotResetTopFocusWindow()
+            throws RemoteException {
+        // At setup, the default display sets be the top focused display and
+        // its current focused window sets be the top focused window.
+        // Starts tracking window of second display.
+        startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+        assertTrue(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID));
+        // Stops tracking windows of second display.
+        mA11yWindowManager.stopTrackingWindows(SECONDARY_DISPLAY_ID);
+        assertNotEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT),
+                AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
+    }
+
+    @Test
     public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() {
         final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
         WindowInfo focusedWindowInfo =
@@ -196,10 +212,66 @@
     }
 
     @Test
+    public void
+            onWindowsChanged_focusChangeOnNonTopFocusedDisplay_perDisplayFocusOn_notChangeWindow()
+            throws RemoteException {
+        // At setup, the default display sets be the top focused display and
+        // its current focused window sets be the top focused window.
+        // Sets supporting multiple focused window, i.e., config_perDisplayFocusEnabled is true.
+        mSupportPerDisplayFocus = true;
+        // Starts tracking window of second display.
+        startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+        // Gets the active window.
+        final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
+        // Gets the top focused window.
+        final int topFocusedWindowId =
+                mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT);
+        // Changes the current focused window at second display.
+        changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID,
+                DEFAULT_FOCUSED_INDEX + 1, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX);
+
+        onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
+        // The active window should not be changed.
+        assertEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
+        // The top focused window should not be changed.
+        assertEquals(topFocusedWindowId,
+                mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT));
+    }
+
+    @Test
+    public void
+            onWindowChange_focusChangeToNonTopFocusedDisplay_perDisplayFocusOff_shouldChangeWindow()
+            throws RemoteException {
+        // At setup, the default display sets be the top focused display and
+        // its current focused window sets be the top focused window.
+        // Sets not supporting multiple focused window, i.e., config_perDisplayFocusEnabled is
+        // false.
+        mSupportPerDisplayFocus = false;
+        // Starts tracking window of second display.
+        startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
+        // Gets the active window.
+        final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID);
+        // Gets the top focused window.
+        final int topFocusedWindowId =
+                mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT);
+        // Changes the current focused window from default display to second display.
+        changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID,
+                DEFAULT_FOCUSED_INDEX, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX);
+
+        onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
+        onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES);
+        // The active window should be changed.
+        assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID));
+        // The top focused window should be changed.
+        assertNotEquals(topFocusedWindowId,
+                mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT));
+    }
+
+    @Test
     public void onWindowsChanged_shouldReportCorrectLayer() {
         // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup.
         List<AccessibilityWindowInfo> a11yWindows =
-                mA11yWindowManager.getWindowListLocked();
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
         for (int i = 0; i < a11yWindows.size(); i++) {
             final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i);
             final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i);
@@ -212,7 +284,7 @@
     public void onWindowsChanged_shouldReportCorrectOrder() {
         // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup.
         List<AccessibilityWindowInfo> a11yWindows =
-                mA11yWindowManager.getWindowListLocked();
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
         for (int i = 0; i < a11yWindows.size(); i++) {
             final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i);
             final IBinder windowToken = mA11yWindowManager
@@ -226,31 +298,31 @@
     public void onWindowsChangedAndForceSend_shouldUpdateWindows() {
         final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
         final int correctLayer =
-                mA11yWindowManager.getWindowListLocked().get(0).getLayer();
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer();
         windowInfo.layer += 1;
 
         onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND);
         assertNotEquals(correctLayer,
-                mA11yWindowManager.getWindowListLocked().get(0).getLayer());
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer());
     }
 
     @Test
     public void onWindowsChangedNoForceSend_layerChanged_shouldNotUpdateWindows() {
         final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
         final int correctLayer =
-                mA11yWindowManager.getWindowListLocked().get(0).getLayer();
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer();
         windowInfo.layer += 1;
 
         onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
         assertEquals(correctLayer,
-                mA11yWindowManager.getWindowListLocked().get(0).getLayer());
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer());
     }
 
     @Test
     public void onWindowsChangedNoForceSend_windowChanged_shouldUpdateWindows()
             throws RemoteException {
         final AccessibilityWindowInfo oldWindow =
-                mA11yWindowManager.getWindowListLocked().get(0);
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0);
         final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
                 true, USER_SYSTEM_ID);
         final WindowInfo windowInfo = WindowInfo.obtain();
@@ -262,7 +334,7 @@
 
         onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
         assertNotEquals(oldWindow,
-                mA11yWindowManager.getWindowListLocked().get(0));
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0));
     }
 
     @Test
@@ -274,7 +346,8 @@
         windowInfo.focused = true;
 
         onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
-        assertTrue(mA11yWindowManager.getWindowListLocked().get(0).isFocused());
+        assertTrue(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0)
+                .isFocused());
     }
 
     @Test
@@ -305,7 +378,7 @@
     @Test
     public void getWindowTokenForUserAndWindowId_shouldNotNull() {
         final List<AccessibilityWindowInfo> windows =
-                mA11yWindowManager.getWindowListLocked();
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
         for (int i = 0; i < windows.size(); i++) {
             final int windowId = windows.get(i).getId();
 
@@ -317,7 +390,7 @@
     @Test
     public void findWindowId() {
         final List<AccessibilityWindowInfo> windows =
-                mA11yWindowManager.getWindowListLocked();
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
         for (int i = 0; i < windows.size(); i++) {
             final int windowId = windows.get(i).getId();
             final IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
@@ -339,7 +412,7 @@
         onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
 
         final List<AccessibilityWindowInfo> a11yWindows =
-                mA11yWindowManager.getWindowListLocked();
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
         final Region outBounds = new Region();
         int windowId = a11yWindows.get(0).getId();
 
@@ -362,7 +435,7 @@
 
         onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
         final List<AccessibilityWindowInfo> a11yWindows =
-                mA11yWindowManager.getWindowListLocked();
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
         final Region outBounds = new Region();
         int windowId = a11yWindows.get(1).getId();
 
@@ -375,7 +448,7 @@
     public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() {
         // Since z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible.
         final List<AccessibilityWindowInfo> a11yWindows =
-                mA11yWindowManager.getWindowListLocked();
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
         final Region outBounds = new Region();
         int windowId = a11yWindows.get(1).getId();
 
@@ -393,7 +466,7 @@
         onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES);
 
         final List<AccessibilityWindowInfo> a11yWindows =
-                mA11yWindowManager.getWindowListLocked();
+                mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY);
         final Region outBounds = new Region();
         int windowId = a11yWindows.get(1).getId();
 
@@ -413,7 +486,7 @@
                 .thenReturn(eventWindowToken);
 
         final int noUse = 0;
-        mA11yWindowManager.stopTrackingWindows();
+        mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY);
         mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
                 eventWindowId,
                 noUse,
@@ -553,7 +626,7 @@
                 mA11yWindowManager.getConnectionLocked(
                         USER_SYSTEM_ID, newFocusWindowId).getRemote();
 
-        mA11yWindowManager.stopTrackingWindows();
+        mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY);
         final int noUse = 0;
         mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID,
                 defaultFocusWindowId,
@@ -636,20 +709,27 @@
                     false, USER_SYSTEM_ID);
             addWindowInfo(windowInfosForDisplay, token, layer++);
         }
-        // Setups default focus.
-        windowInfosForDisplay.get(DEFAULT_FOCUSED_INDEX).focused = true;
+        // Sets up current focused window of display.
+        // Each display has its own current focused window if config_perDisplayFocusEnabled is true.
+        // Otherwise only default display needs to current focused window.
+        if (mSupportPerDisplayFocus || displayId == Display.DEFAULT_DISPLAY) {
+            windowInfosForDisplay.get(DEFAULT_FOCUSED_INDEX).focused = true;
+        }
         // Turns on windows tracking, and update window info.
-        mA11yWindowManager.startTrackingWindows();
+        when(mMockWindowManagerInternal.setWindowsForAccessibilityCallback(eq(displayId), any()))
+                .thenReturn(true);
+        mA11yWindowManager.startTrackingWindows(displayId);
         // Puts window lists into array.
         mWindowInfos.put(displayId, windowInfosForDisplay);
-        // Sets the default display as the top focused display.
+        // Sets the default display is the top focused display and
+        // its current focused window is the top focused window.
         if (displayId == Display.DEFAULT_DISPLAY) {
             setTopFocusedWindowAndDisplay(displayId, DEFAULT_FOCUSED_INDEX);
         }
         // Invokes callback for sending window lists to A11y framework.
         onWindowsForAccessibilityChanged(displayId, FORCE_SEND);
 
-        assertEquals(mA11yWindowManager.getWindowListLocked().size(),
+        assertEquals(mA11yWindowManager.getWindowListLocked(displayId).size(),
                 windowInfosForDisplay.size());
     }
 
@@ -700,12 +780,9 @@
 
     private void setTopFocusedWindowAndDisplay(int displayId, int index) {
         // Sets the top focus window.
-        final IBinder eventWindowToken = mWindowInfos.get(displayId).get(index).token;
-        when(mMockWindowManagerInternal.getFocusedWindowToken())
-                .thenReturn(eventWindowToken);
+        mTopFocusedWindowToken = mWindowInfos.get(displayId).get(index).token;
         // Sets the top focused display.
-        when(mMockWindowManagerInternal.getDisplayIdForWindow(eventWindowToken))
-                .thenReturn(displayId);
+        mTopFocusedDisplayId = displayId;
     }
 
     private void onWindowsForAccessibilityChanged(int displayId, boolean forceSend) {
@@ -714,7 +791,38 @@
             callbacks = getWindowsForAccessibilityCallbacks(displayId);
             mCallbackOfWindows.put(displayId, callbacks);
         }
-        callbacks.onWindowsForAccessibilityChanged(forceSend, mWindowInfos.get(displayId));
+        callbacks.onWindowsForAccessibilityChanged(forceSend, mTopFocusedDisplayId,
+                mTopFocusedWindowToken, mWindowInfos.get(displayId));
+    }
+
+    private void changeFocusedWindowOnDisplayPerDisplayFocusConfig(
+            int changeFocusedDisplayId, int newFocusedWindowIndex, int oldTopFocusedDisplayId,
+            int oldFocusedWindowIndex) {
+        if (mSupportPerDisplayFocus) {
+            // Gets the old focused window of display which wants to change focused window.
+            WindowInfo focusedWindowInfo =
+                    mWindowInfos.get(changeFocusedDisplayId).get(oldFocusedWindowIndex);
+            // Resets the focus of old focused window.
+            focusedWindowInfo.focused = false;
+            // Gets the new window of display which wants to change focused window.
+            focusedWindowInfo =
+                    mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex);
+            // Sets the focus of new focused window.
+            focusedWindowInfo.focused = true;
+        } else {
+            // Gets the window of display which wants to change focused window.
+            WindowInfo focusedWindowInfo =
+                    mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex);
+            // Sets the focus of new focused window.
+            focusedWindowInfo.focused = true;
+            // Gets the old focused window of old top focused display.
+            focusedWindowInfo =
+                    mWindowInfos.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex);
+            // Resets the focus of old focused window.
+            focusedWindowInfo.focused = false;
+            // Changes the top focused display and window.
+            setTopFocusedWindowAndDisplay(changeFocusedDisplayId, newFocusedWindowIndex);
+        }
     }
 
     static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
index 65c5781..41142f6 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsActiveWatcherTest.java
@@ -57,15 +57,15 @@
 
         // Start watching active ops
         final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
-        appOpsManager.startWatchingActive(new int[] {AppOpsManager.OP_CAMERA,
-                AppOpsManager.OP_RECORD_AUDIO}, listener);
+        appOpsManager.startWatchingActive(new String[] {AppOpsManager.OPSTR_CAMERA,
+                AppOpsManager.OPSTR_RECORD_AUDIO}, getContext().getMainExecutor(), listener);
 
         // Start the op
         appOpsManager.startOp(AppOpsManager.OP_CAMERA);
 
         // Verify that we got called for the op being active
         verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
-                .times(1)).onOpActiveChanged(eq(AppOpsManager.OP_CAMERA),
+                .times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA),
                 eq(Process.myUid()), eq(getContext().getPackageName()), eq(true));
 
         // This should be the only callback we got
@@ -83,7 +83,7 @@
 
         // Verify that we got called for the op being active
         verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
-                .times(1)).onOpActiveChanged(eq(AppOpsManager.OP_CAMERA),
+                .times(1)).onOpActiveChanged(eq(AppOpsManager.OPSTR_CAMERA),
                 eq(Process.myUid()), eq(getContext().getPackageName()), eq(false));
 
         // Verify that the op is not active
@@ -155,4 +155,4 @@
     private static Context getContext() {
         return InstrumentationRegistry.getContext();
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 8f8b746..365cd80 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -22,6 +22,8 @@
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 
+import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
+
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.fail;
 
@@ -2690,4 +2692,51 @@
         assertFalse(mHelper.areBubblesAllowed(PKG_O, UID_O));
         verify(mHandler, times(1)).requestSort();
     }
+
+    @Test
+    public void testTooManyChannels() {
+        for (int i = 0; i < NOTIFICATION_CHANNEL_COUNT_LIMIT; i++) {
+            NotificationChannel channel = new NotificationChannel(String.valueOf(i),
+                    String.valueOf(i), NotificationManager.IMPORTANCE_HIGH);
+            mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, true);
+        }
+        try {
+            NotificationChannel channel = new NotificationChannel(
+                    String.valueOf(NOTIFICATION_CHANNEL_COUNT_LIMIT),
+                    String.valueOf(NOTIFICATION_CHANNEL_COUNT_LIMIT),
+                    NotificationManager.IMPORTANCE_HIGH);
+            mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, true);
+            fail("Allowed to create too many notification channels");
+        } catch (IllegalStateException e) {
+            // great
+        }
+    }
+
+    @Test
+    public void testTooManyChannels_xml() throws Exception {
+        String extraChannel = "EXTRA";
+        String extraChannel1 = "EXTRA1";
+
+        // create first... many... directly so we don't need a big xml blob in this test
+        for (int i = 0; i < NOTIFICATION_CHANNEL_COUNT_LIMIT; i++) {
+            NotificationChannel channel = new NotificationChannel(String.valueOf(i),
+                    String.valueOf(i), NotificationManager.IMPORTANCE_HIGH);
+            mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, true);
+        }
+
+        final String xml = "<ranking version=\"1\">\n"
+                + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+                + "<channel id=\"" + extraChannel + "\" name=\"hi\" importance=\"3\"/>"
+                + "<channel id=\"" + extraChannel1 + "\" name=\"hi\" importance=\"3\"/>"
+                + "</package>"
+                + "</ranking>";
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+                null);
+        parser.nextTag();
+        mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+        assertNull(mHelper.getNotificationChannel(PKG_O, UID_O, extraChannel, true));
+        assertNull(mHelper.getNotificationChannel(PKG_O, UID_O, extraChannel1, true));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index eaffd77..13748cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -43,6 +43,7 @@
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REQUESTED;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
 import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
 import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
@@ -1047,8 +1048,7 @@
 
         assertEquals(DESTROYING, mActivity.getState());
         assertTrue(mActivity.finishing);
-        verify(mStack).destroyActivityLocked(eq(mActivity), eq(true) /* removeFromApp */,
-                anyString());
+        verify(mActivity).destroyImmediately(eq(true) /* removeFromApp */, anyString());
     }
 
     /**
@@ -1072,12 +1072,140 @@
 
         // Verify that the activity was not actually destroyed, but waits for next one to come up
         // instead.
-        verify(mStack, never()).destroyActivityLocked(eq(mActivity), eq(true) /* removeFromApp */,
-                anyString());
+        verify(mActivity, never()).destroyImmediately(eq(true) /* removeFromApp */, anyString());
         assertEquals(FINISHING, mActivity.getState());
         assertTrue(mActivity.mStackSupervisor.mFinishingActivities.contains(mActivity));
     }
 
+    /**
+     * Test that the activity will be moved to destroying state and the message to destroy will be
+     * sent to the client.
+     */
+    @Test
+    public void testDestroyImmediately_hadApp_finishing() {
+        mActivity.finishing = true;
+        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+
+        assertEquals(DESTROYING, mActivity.getState());
+    }
+
+    /**
+     * Test that the activity will be moved to destroyed state immediately if it was not marked as
+     * finishing before {@link ActivityRecord#destroyImmediately(boolean, String)}.
+     */
+    @Test
+    public void testDestroyImmediately_hadApp_notFinishing() {
+        mActivity.finishing = false;
+        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+
+        assertEquals(DESTROYED, mActivity.getState());
+    }
+
+    /**
+     * Test that an activity with no process attached and that is marked as finishing will be
+     * removed from task when {@link ActivityRecord#destroyImmediately(boolean, String)} is called.
+     */
+    @Test
+    public void testDestroyImmediately_noApp_finishing() {
+        mActivity.app = null;
+        mActivity.finishing = true;
+        final TaskRecord task = mActivity.getTaskRecord();
+
+        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+
+        assertEquals(DESTROYED, mActivity.getState());
+        assertNull(mActivity.getTaskRecord());
+        assertEquals(0, task.getChildCount());
+    }
+
+    /**
+     * Test that an activity with no process attached and that is not marked as finishing will be
+     * marked as DESTROYED but not removed from task.
+     */
+    @Test
+    public void testDestroyImmediately_noApp_notFinishing() {
+        mActivity.app = null;
+        mActivity.finishing = false;
+        final TaskRecord task = mActivity.getTaskRecord();
+
+        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+
+        assertEquals(DESTROYED, mActivity.getState());
+        assertEquals(task, mActivity.getTaskRecord());
+        assertEquals(1, task.getChildCount());
+    }
+
+    /**
+     * Test that an activity will not be destroyed if it is marked as non-destroyable.
+     */
+    @Test
+    public void testSafelyDestroy_nonDestroyable() {
+        doReturn(false).when(mActivity).isDestroyable();
+
+        mActivity.safelyDestroy("test");
+
+        verify(mActivity, never()).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+    }
+
+    /**
+     * Test that an activity will not be destroyed if it is marked as non-destroyable.
+     */
+    @Test
+    public void testSafelyDestroy_destroyable() {
+        doReturn(true).when(mActivity).isDestroyable();
+
+        mActivity.safelyDestroy("test");
+
+        verify(mActivity).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+    }
+
+    @Test
+    public void testRemoveFromHistory() {
+        final ActivityStack stack = mActivity.getActivityStack();
+        final TaskRecord task = mActivity.getTaskRecord();
+
+        mActivity.removeFromHistory("test");
+
+        assertEquals(DESTROYED, mActivity.getState());
+        assertNull(mActivity.app);
+        assertNull(mActivity.getTaskRecord());
+        assertEquals(0, task.getChildCount());
+        assertNull(task.getStack());
+        assertEquals(0, stack.getChildCount());
+    }
+
+    /**
+     * Test that it's not allowed to call {@link ActivityRecord#destroyed(String)} if activity is
+     * not in destroying or destroyed state.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testDestroyed_notDestroying() {
+        mActivity.setState(STOPPED, "test");
+        mActivity.destroyed("test");
+    }
+
+    /**
+     * Test that {@link ActivityRecord#destroyed(String)} can be called if an activity is destroying
+     */
+    @Test
+    public void testDestroyed_destroying() {
+        mActivity.setState(DESTROYING, "test");
+        mActivity.destroyed("test");
+
+        verify(mActivity).removeFromHistory(anyString());
+    }
+
+    /**
+     * Test that {@link ActivityRecord#destroyed(String)} can be called if an activity is destroyed.
+     */
+    @Test
+    public void testDestroyed_destroyed() {
+        mActivity.setState(DESTROYED, "test");
+        mActivity.destroyed("test");
+
+        verify(mActivity).removeFromHistory(anyString());
+    }
+
     /** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */
     private void prepareFixedAspectRatioUnresizableActivity() {
         setupDisplayContentForCompatDisplayInsets();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index d3194508..60c5f0b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -233,7 +233,7 @@
         final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
         r.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
         mStack.moveToFront("testStopActivityWithDestroy");
-        mStack.stopActivityLocked(r);
+        r.stopIfPossible();
         // Mostly testing to make sure there is a crash in the call part, so if we get here we are
         // good-to-go!
     }
@@ -879,7 +879,7 @@
         final ActivityRecord overlayActivity = new ActivityBuilder(mService).setTask(mTask)
                 .setComponent(new ComponentName("package.overlay", ".OverlayActivity")).build();
         // If the task only remains overlay activity, the task should also be removed.
-        // See {@link ActivityStack#removeActivityFromHistoryLocked}.
+        // See {@link ActivityStack#removeFromHistory}.
         overlayActivity.mTaskOverlay = true;
 
         // The activity without an app means it will be removed immediately.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index bcff704..e6c9b9f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -249,6 +249,20 @@
     }
 
     @Test
+    public void testInheritsFreeformModeFromSourceOnFullscreenDisplay() {
+        final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FULLSCREEN);
+        final ActivityRecord source = createSourceActivity(fullscreenDisplay);
+        source.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, source, /* options */ null, mCurrent, mResult));
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+                WINDOWING_MODE_FULLSCREEN);
+    }
+
+    @Test
     public void testKeepsPictureInPictureLaunchModeInOptions() {
         final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
                 WINDOWING_MODE_FREEFORM);
@@ -571,6 +585,23 @@
     }
 
     @Test
+    public void testRespectsLaunchBoundsWithFreeformSourceOnFullscreenDisplay() {
+        final TestActivityDisplay fullscreenDisplay = createNewActivityDisplay(
+                WINDOWING_MODE_FULLSCREEN);
+        final ActivityRecord source = createSourceActivity(fullscreenDisplay);
+        source.setWindowingMode(WINDOWING_MODE_FREEFORM);
+
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        final Rect expected = new Rect(0, 0, 150, 150);
+        options.setLaunchBounds(expected);
+
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
+                mActivity, source, options, mCurrent, mResult));
+
+        assertEquals(expected, mResult.mBounds);
+    }
+
+    @Test
     public void testNonEmptyLayoutBoundsRespectsGravityWithEmptySize_LeftGravity() {
         final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
                 WINDOWING_MODE_FREEFORM);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7f7a78b..9f3b07b 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -250,7 +250,7 @@
     }
 
     public UsbDeviceManager(Context context, UsbAlsaManager alsaManager,
-            UsbSettingsManager settingsManager) {
+            UsbSettingsManager settingsManager, UsbPermissionManager permissionManager) {
         mContext = context;
         mContentResolver = context.getContentResolver();
         PackageManager pm = mContext.getPackageManager();
@@ -284,13 +284,13 @@
              * Initialze the legacy UsbHandler
              */
             mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext, this,
-                    alsaManager, settingsManager);
+                    alsaManager, permissionManager);
         } else {
             /**
              * Initialize HAL based UsbHandler
              */
             mHandler = new UsbHandlerHal(FgThread.get().getLooper(), mContext, this,
-                    alsaManager, settingsManager);
+                    alsaManager, permissionManager);
         }
 
         if (nativeIsStartRequested()) {
@@ -468,7 +468,7 @@
 
         private final Context mContext;
         private final UsbAlsaManager mUsbAlsaManager;
-        private final UsbSettingsManager mSettingsManager;
+        private final UsbPermissionManager mPermissionManager;
         private NotificationManager mNotificationManager;
 
         protected long mScreenUnlockedFunctions;
@@ -489,12 +489,12 @@
         protected static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
 
         UsbHandler(Looper looper, Context context, UsbDeviceManager deviceManager,
-                UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
+                UsbAlsaManager alsaManager, UsbPermissionManager permissionManager) {
             super(looper);
             mContext = context;
             mUsbDeviceManager = deviceManager;
             mUsbAlsaManager = alsaManager;
-            mSettingsManager = settingsManager;
+            mPermissionManager = permissionManager;
             mContentResolver = context.getContentResolver();
 
             mCurrentUser = ActivityManager.getCurrentUser();
@@ -625,7 +625,7 @@
                 // successfully entered accessory mode
                 String[] accessoryStrings = mUsbDeviceManager.getAccessoryStrings();
                 if (accessoryStrings != null) {
-                    UsbSerialReader serialReader = new UsbSerialReader(mContext, mSettingsManager,
+                    UsbSerialReader serialReader = new UsbSerialReader(mContext, mPermissionManager,
                             accessoryStrings[UsbAccessory.SERIAL_STRING]);
 
                     mCurrentAccessory = new UsbAccessory(
@@ -663,7 +663,7 @@
 
             if (mCurrentAccessory != null) {
                 if (mBootCompleted) {
-                    mSettingsManager.usbAccessoryRemoved(mCurrentAccessory);
+                    mPermissionManager.usbAccessoryRemoved(mCurrentAccessory);
                 }
                 mCurrentAccessory = null;
             }
@@ -1343,8 +1343,8 @@
         private boolean mUsbDataUnlocked;
 
         UsbHandlerLegacy(Looper looper, Context context, UsbDeviceManager deviceManager,
-                UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
-            super(looper, context, deviceManager, alsaManager, settingsManager);
+                UsbAlsaManager alsaManager, UsbPermissionManager permissionManager) {
+            super(looper, context, deviceManager, alsaManager, permissionManager);
             try {
                 readOemUsbOverrideConfig(context);
                 // Restore default functions.
@@ -1738,8 +1738,8 @@
         protected boolean mCurrentUsbFunctionsRequested;
 
         UsbHandlerHal(Looper looper, Context context, UsbDeviceManager deviceManager,
-                UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
-            super(looper, context, deviceManager, alsaManager, settingsManager);
+                UsbAlsaManager alsaManager, UsbPermissionManager permissionManager) {
+            super(looper, context, deviceManager, alsaManager, permissionManager);
             try {
                 ServiceNotification serviceNotification = new ServiceNotification();
 
@@ -1977,7 +1977,7 @@
      * @param uid Uid of the caller
      */
     public ParcelFileDescriptor openAccessory(UsbAccessory accessory,
-            UsbUserSettingsManager settings, int uid) {
+            UsbUserPermissionManager permissions, int uid) {
         UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
         if (currentAccessory == null) {
             throw new IllegalArgumentException("no accessory attached");
@@ -1988,7 +1988,7 @@
                     + currentAccessory;
             throw new IllegalArgumentException(error);
         }
-        settings.checkPermission(accessory, uid);
+        permissions.checkPermission(accessory, uid);
         return nativeOpenAccessory();
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 00c7548..e899dff 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -65,7 +65,7 @@
     private final String[] mHostBlacklist;
 
     private final UsbAlsaManager mUsbAlsaManager;
-    private final UsbSettingsManager mSettingsManager;
+    private final UsbPermissionManager mPermissionManager;
 
     private final Object mLock = new Object();
     @GuardedBy("mLock")
@@ -232,13 +232,13 @@
      * UsbHostManager
      */
     public UsbHostManager(Context context, UsbAlsaManager alsaManager,
-            UsbSettingsManager settingsManager) {
+            UsbPermissionManager permissionManager) {
         mContext = context;
 
         mHostBlacklist = context.getResources().getStringArray(
                 com.android.internal.R.array.config_usbHostBlacklist);
         mUsbAlsaManager = alsaManager;
-        mSettingsManager = settingsManager;
+        mPermissionManager = permissionManager;
         String deviceConnectionHandler = context.getResources().getString(
                 com.android.internal.R.string.config_UsbDeviceConnectionHandling_component);
         if (!TextUtils.isEmpty(deviceConnectionHandler)) {
@@ -393,8 +393,8 @@
                 addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE,
                         parser.getRawDescriptors());
             } else {
-                UsbSerialReader serialNumberReader = new UsbSerialReader(mContext, mSettingsManager,
-                        newDeviceBuilder.serialNumber);
+                UsbSerialReader serialNumberReader = new UsbSerialReader(mContext,
+                        mPermissionManager, newDeviceBuilder.serialNumber);
                 UsbDevice newDevice = newDeviceBuilder.build(serialNumberReader);
                 serialNumberReader.setDevice(newDevice);
 
@@ -444,7 +444,7 @@
             if (device != null) {
                 Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName());
                 mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
-                mSettingsManager.usbDeviceRemoved(device);
+                mPermissionManager.usbDeviceRemoved(device);
                 getCurrentUserSettings().usbDeviceRemoved(device);
                 ConnectionRecord current = mConnected.get(deviceAddress);
                 // Tracking
@@ -484,9 +484,11 @@
         }
     }
 
-    /* Opens the specified USB device */
-    public ParcelFileDescriptor openDevice(String deviceAddress, UsbUserSettingsManager settings,
-            String packageName, int uid) {
+    /**
+     *  Opens the specified USB device
+     */
+    public ParcelFileDescriptor openDevice(String deviceAddress,
+            UsbUserPermissionManager permissions, String packageName, int uid) {
         synchronized (mLock) {
             if (isBlackListed(deviceAddress)) {
                 throw new SecurityException("USB device is on a restricted bus");
@@ -498,7 +500,7 @@
                         "device " + deviceAddress + " does not exist or is restricted");
             }
 
-            settings.checkPermission(device, packageName, uid);
+            permissions.checkPermission(device, packageName, uid);
             return nativeOpenDevice(deviceAddress);
         }
     }
diff --git a/services/usb/java/com/android/server/usb/UsbPermissionManager.java b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
index dd2f29b..14c7c7c 100644
--- a/services/usb/java/com/android/server/usb/UsbPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,230 +17,95 @@
 package com.android.server.usb;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
+import android.annotation.UserIdInt;
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
-import android.os.Binder;
-import android.os.Process;
 import android.os.UserHandle;
-import android.service.usb.UsbSettingsAccessoryPermissionProto;
-import android.service.usb.UsbSettingsDevicePermissionProto;
-import android.service.usb.UsbUserSettingsManagerProto;
 import android.util.Slog;
-import android.util.SparseBooleanArray;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.dump.DualDumpOutputStream;
 
-import java.util.HashMap;
-
-/**
- * UsbPermissionManager manages usb device or accessory access permissions.
- *
- * @hide
- */
 class UsbPermissionManager {
     private static final String LOG_TAG = UsbPermissionManager.class.getSimpleName();
+    private static final boolean DEBUG = false;
 
-    @GuardedBy("mLock")
-    /** Temporary mapping USB device name to list of UIDs with permissions for the device*/
-    private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
-            new HashMap<>();
-    @GuardedBy("mLock")
-    /** Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory*/
-    private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
-            new HashMap<>();
+    /** Context to be used by this module */
+    private final @NonNull Context mContext;
 
-    private final UserHandle mUser;
-    private final boolean mDisablePermissionDialogs;
+    /** Map from user id to {@link UsbUserPermissionManager} for the user */
+    @GuardedBy("mPermissionsByUser")
+    private final SparseArray<UsbUserPermissionManager> mPermissionsByUser = new SparseArray<>();
 
-    private final Object mLock = new Object();
+    final UsbService mUsbService;
 
-    UsbPermissionManager(@NonNull Context context, @NonNull UserHandle user) {
-        mUser = user;
-        mDisablePermissionDialogs = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+    UsbPermissionManager(@NonNull Context context,
+            @NonNull UsbService usbService) {
+        mContext = context;
+        mUsbService = usbService;
     }
 
-    /**
-     * Removes access permissions of all packages for the USB accessory.
-     *
-     * @param accessory to remove permissions for
-     */
-    void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
-        synchronized (mLock) {
-            mAccessoryPermissionMap.remove(accessory);
+    @NonNull UsbUserPermissionManager getPermissionsForUser(@UserIdInt int userId) {
+        synchronized (mPermissionsByUser) {
+            UsbUserPermissionManager permissions = mPermissionsByUser.get(userId);
+            if (permissions == null) {
+                permissions = new UsbUserPermissionManager(mContext, UserHandle.of(userId),
+                        mUsbService.getSettingsForUser(userId));
+                mPermissionsByUser.put(userId, permissions);
+            }
+            return permissions;
+        }
+    }
+
+    void remove(@NonNull UserHandle userToRemove) {
+        synchronized (mPermissionsByUser) {
+            mPermissionsByUser.remove(userToRemove.getIdentifier());
         }
     }
 
     /**
-     * Removes access permissions of all packages for the USB device.
+     * Remove temporary access permission and broadcast that a device was removed.
      *
-     * @param device to remove permissions for
+     * @param device The device that is removed
      */
-    void removeDevicePermissions(@NonNull UsbDevice device) {
-        synchronized (mLock) {
-            mDevicePermissionMap.remove(device.getDeviceName());
+    void usbDeviceRemoved(@NonNull UsbDevice device) {
+        synchronized (mPermissionsByUser) {
+            for (int i = 0; i < mPermissionsByUser.size(); i++) {
+                // clear temporary permissions for the device
+                mPermissionsByUser.valueAt(i).removeDevicePermissions(device);
+            }
         }
+
+        Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
+        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+
+        if (DEBUG) {
+            Slog.d(LOG_TAG, "usbDeviceRemoved, sending " + intent);
+        }
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     /**
-     * Grants permission for USB device without showing system dialog for package with uid.
+     * Remove temporary access permission and broadcast that a accessory was removed.
      *
-     * @param device to grant permission for
-     * @param uid to grant permission for
+     * @param accessory The accessory that is removed
      */
-    void grantDevicePermission(@NonNull UsbDevice device, int uid) {
-        synchronized (mLock) {
-            String deviceName = device.getDeviceName();
-            SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
-            if (uidList == null) {
-                uidList = new SparseBooleanArray(1);
-                mDevicePermissionMap.put(deviceName, uidList);
+    void usbAccessoryRemoved(@NonNull UsbAccessory accessory) {
+        synchronized (mPermissionsByUser) {
+            for (int i = 0; i < mPermissionsByUser.size(); i++) {
+                // clear temporary permissions for the accessory
+                mPermissionsByUser.valueAt(i).removeAccessoryPermissions(accessory);
             }
-            uidList.put(uid, true);
         }
+
+        Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
+        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
     }
 
-    /**
-     * Grants permission for USB accessory without showing system dialog for package with uid.
-     *
-     * @param accessory to grant permission for
-     * @param uid to grant permission for
-     */
-    void grantAccessoryPermission(@NonNull UsbAccessory accessory, int uid) {
-        synchronized (mLock) {
-            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
-            if (uidList == null) {
-                uidList = new SparseBooleanArray(1);
-                mAccessoryPermissionMap.put(accessory, uidList);
-            }
-            uidList.put(uid, true);
-        }
-    }
-
-    /**
-     * Returns true if package with uid has permission to access the device.
-     *
-     * @param device to check permission for
-     * @param uid to check permission for
-     * @return {@code true} if package with uid has permission
-     */
-    boolean hasPermission(@NonNull UsbDevice device, int uid) {
-        synchronized (mLock) {
-            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
-                return true;
-            }
-            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
-            if (uidList == null) {
-                return false;
-            }
-            return uidList.get(uid);
-        }
-    }
-
-    /**
-     * Returns true if caller has permission to access the accessory.
-     *
-     * @param accessory to check permission for
-     * @param uid to check permission for
-     * @return {@code true} if caller has permssion
-     */
-    boolean hasPermission(@NonNull UsbAccessory accessory, int uid) {
-        synchronized (mLock) {
-            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
-                return true;
-            }
-            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
-            if (uidList == null) {
-                return false;
-            }
-            return uidList.get(uid);
-        }
-    }
-
-    /**
-     * Creates UI dialog to request permission for the given package to access the device
-     * or accessory.
-     *
-     * @param device The USB device attached
-     * @param accessory The USB accessory attached
-     * @param canBeDefault Whether the calling pacakge can set as default handler
-     * of the USB device or accessory
-     * @param packageName The package name of the calling package
-     * @param uid The uid of the calling package
-     * @param userContext The context to start the UI dialog
-     * @param pi PendingIntent for returning result
-     */
-    void requestPermissionDialog(@Nullable UsbDevice device,
-                                 @Nullable UsbAccessory accessory,
-                                 boolean canBeDefault,
-                                 @NonNull String packageName,
-                                 int uid,
-                                 @NonNull Context userContext,
-                                 @NonNull PendingIntent pi) {
-        long identity = Binder.clearCallingIdentity();
-        Intent intent = new Intent();
-        if (device != null) {
-            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-        } else {
-            intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-        }
-        intent.putExtra(Intent.EXTRA_INTENT, pi);
-        intent.putExtra(Intent.EXTRA_UID, uid);
-        intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
-        intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
-        intent.setClassName("com.android.systemui",
-                "com.android.systemui.usb.UsbPermissionActivity");
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        try {
-            userContext.startActivityAsUser(intent, mUser);
-        } catch (ActivityNotFoundException e) {
-            Slog.e(LOG_TAG, "unable to start UsbPermissionActivity");
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    void dump(@NonNull DualDumpOutputStream dump) {
-        synchronized (mLock) {
-            for (String deviceName : mDevicePermissionMap.keySet()) {
-                long devicePermissionToken = dump.start("device_permissions",
-                        UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
-
-                dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
-
-                SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
-                int count = uidList.size();
-                for (int i = 0; i < count; i++) {
-                    dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
-                }
-
-                dump.end(devicePermissionToken);
-            }
-
-            for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
-                long accessoryPermissionToken = dump.start("accessory_permissions",
-                        UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
-
-                dump.write("accessory_description",
-                        UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
-                        accessory.getDescription());
-
-                SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
-                int count = uidList.size();
-                for (int i = 0; i < count; i++) {
-                    dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
-                }
-
-                dump.end(accessoryPermissionToken);
-            }
-        }
-    }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index 74c3939..5e136bb 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -783,7 +783,7 @@
             return;
         }
 
-        mSettingsManager.getSettingsForUser(UserHandle.getUserId(appInfo.uid))
+        mSettingsManager.mUsbService.getPermissionsForUser(UserHandle.getUserId(appInfo.uid))
                 .grantDevicePermission(device, appInfo.uid);
 
         Intent activityIntent = new Intent(intent);
@@ -844,14 +844,15 @@
         }
 
         if (defaultActivity != null) {
-            UsbUserSettingsManager defaultRIUserSettings = mSettingsManager.getSettingsForUser(
-                    UserHandle.getUserId(defaultActivity.applicationInfo.uid));
+            UsbUserPermissionManager defaultRIUserPermissions =
+                    mSettingsManager.mUsbService.getPermissionsForUser(
+                            UserHandle.getUserId(defaultActivity.applicationInfo.uid));
             // grant permission for default activity
             if (device != null) {
-                defaultRIUserSettings.
-                        grantDevicePermission(device, defaultActivity.applicationInfo.uid);
+                defaultRIUserPermissions
+                        .grantDevicePermission(device, defaultActivity.applicationInfo.uid);
             } else if (accessory != null) {
-                defaultRIUserSettings.grantAccessoryPermission(accessory,
+                defaultRIUserPermissions.grantAccessoryPermission(accessory,
                         defaultActivity.applicationInfo.uid);
             }
 
diff --git a/services/usb/java/com/android/server/usb/UsbSerialReader.java b/services/usb/java/com/android/server/usb/UsbSerialReader.java
index 8ca77f0..3151679 100644
--- a/services/usb/java/com/android/server/usb/UsbSerialReader.java
+++ b/services/usb/java/com/android/server/usb/UsbSerialReader.java
@@ -39,7 +39,7 @@
 class UsbSerialReader extends IUsbSerialReader.Stub {
     private final @Nullable String mSerialNumber;
     private final @NonNull Context mContext;
-    private final @NonNull UsbSettingsManager mSettingsManager;
+    private final @NonNull UsbPermissionManager mPermissionManager;
 
     private Object mDevice;
 
@@ -51,10 +51,10 @@
      * @param settingsManager The USB settings manager
      * @param serialNumber The serial number that might be read
      */
-    UsbSerialReader(@NonNull Context context, @NonNull UsbSettingsManager settingsManager,
+    UsbSerialReader(@NonNull Context context, @NonNull UsbPermissionManager permissionManager,
             @Nullable String serialNumber) {
         mContext = context;
-        mSettingsManager = settingsManager;
+        mPermissionManager = permissionManager;
         mSerialNumber = serialNumber;
     }
 
@@ -89,13 +89,14 @@
                 if (packageTargetSdkVersion >= Build.VERSION_CODES.Q) {
                     if (mContext.checkPermission(android.Manifest.permission.MANAGE_USB, pid, uid)
                             == PackageManager.PERMISSION_DENIED) {
-                        UsbUserSettingsManager settings = mSettingsManager.getSettingsForUser(
-                                UserHandle.getUserId(uid));
 
+                        int userId = UserHandle.getUserId(uid);
                         if (mDevice instanceof UsbDevice) {
-                            settings.checkPermission((UsbDevice) mDevice, packageName, uid);
+                            mPermissionManager.getPermissionsForUser(userId)
+                                    .checkPermission((UsbDevice) mDevice, packageName, uid);
                         } else {
-                            settings.checkPermission((UsbAccessory) mDevice, uid);
+                            mPermissionManager.getPermissionsForUser(userId)
+                                    .checkPermission((UsbAccessory) mDevice, uid);
                         }
                     }
                 }
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 4be68b8..cc1490e 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -121,6 +121,7 @@
     private final UsbAlsaManager mAlsaManager;
 
     private final UsbSettingsManager mSettingsManager;
+    private final UsbPermissionManager mPermissionManager;
 
     /**
      * The user id of the current user. There might be several profiles (with separate user ids)
@@ -131,23 +132,35 @@
 
     private final Object mLock = new Object();
 
-    private UsbUserSettingsManager getSettingsForUser(@UserIdInt int userIdInt) {
-        return mSettingsManager.getSettingsForUser(userIdInt);
+    /**
+     * @return the {@link UsbUserSettingsManager} for the given userId
+     */
+    UsbUserSettingsManager getSettingsForUser(@UserIdInt int userId) {
+        return mSettingsManager.getSettingsForUser(userId);
+    }
+
+    /**
+     * @return the {@link UsbUserPermissionManager} for the given userId
+     */
+    UsbUserPermissionManager getPermissionsForUser(@UserIdInt int userId) {
+        return mPermissionManager.getPermissionsForUser(userId);
     }
 
     public UsbService(Context context) {
         mContext = context;
 
         mUserManager = context.getSystemService(UserManager.class);
-        mSettingsManager = new UsbSettingsManager(context);
+        mSettingsManager = new UsbSettingsManager(context, this);
+        mPermissionManager = new UsbPermissionManager(context, this);
         mAlsaManager = new UsbAlsaManager(context);
 
         final PackageManager pm = mContext.getPackageManager();
         if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
-            mHostManager = new UsbHostManager(context, mAlsaManager, mSettingsManager);
+            mHostManager = new UsbHostManager(context, mAlsaManager, mPermissionManager);
         }
         if (new File("/sys/class/android_usb").exists()) {
-            mDeviceManager = new UsbDeviceManager(context, mAlsaManager, mSettingsManager);
+            mDeviceManager = new UsbDeviceManager(context, mAlsaManager, mSettingsManager,
+                    mPermissionManager);
         }
         if (mHostManager != null || mDeviceManager != null) {
             mPortManager = new UsbPortManager(context);
@@ -255,7 +268,7 @@
                 try {
                     synchronized (mLock) {
                         if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) {
-                            fd = mHostManager.openDevice(deviceName, getSettingsForUser(user),
+                            fd = mHostManager.openDevice(deviceName, getPermissionsForUser(user),
                                     packageName, uid);
                         } else {
                             Slog.w(TAG, "Cannot open " + deviceName + " for user " + user
@@ -292,7 +305,7 @@
             try {
                 synchronized (mLock) {
                     if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) {
-                        return mDeviceManager.openAccessory(accessory, getSettingsForUser(user),
+                        return mDeviceManager.openAccessory(accessory, getPermissionsForUser(user),
                                 uid);
                     } else {
                         Slog.w(TAG, "Cannot open " + accessory + " for user " + user
@@ -354,7 +367,7 @@
 
         final long token = Binder.clearCallingIdentity();
         try {
-            return getSettingsForUser(userId).hasPermission(device, packageName, uid);
+            return getPermissionsForUser(userId).hasPermission(device, packageName, uid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -367,7 +380,7 @@
 
         final long token = Binder.clearCallingIdentity();
         try {
-            return getSettingsForUser(userId).hasPermission(accessory, uid);
+            return getPermissionsForUser(userId).hasPermission(accessory, uid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -380,7 +393,7 @@
 
         final long token = Binder.clearCallingIdentity();
         try {
-            getSettingsForUser(userId).requestPermission(device, packageName, pi, uid);
+            getPermissionsForUser(userId).requestPermission(device, packageName, pi, uid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -394,7 +407,7 @@
 
         final long token = Binder.clearCallingIdentity();
         try {
-            getSettingsForUser(userId).requestPermission(accessory, packageName, pi, uid);
+            getPermissionsForUser(userId).requestPermission(accessory, packageName, pi, uid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -407,7 +420,7 @@
 
         final long token = Binder.clearCallingIdentity();
         try {
-            getSettingsForUser(userId).grantDevicePermission(device, uid);
+            getPermissionsForUser(userId).grantDevicePermission(device, uid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -420,7 +433,7 @@
 
         final long token = Binder.clearCallingIdentity();
         try {
-            getSettingsForUser(userId).grantAccessoryPermission(accessory, uid);
+            getPermissionsForUser(userId).grantAccessoryPermission(accessory, uid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 27566f0..fbd8782a 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -19,15 +19,10 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.UserInfo;
-import android.hardware.usb.UsbAccessory;
-import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.usb.UsbSettingsManagerProto;
-import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -59,8 +54,11 @@
     private UserManager mUserManager;
     private UsbHandlerManager mUsbHandlerManager;
 
-    public UsbSettingsManager(@NonNull Context context) {
+    final UsbService mUsbService;
+
+    UsbSettingsManager(@NonNull Context context, UsbService usbService) {
         mContext = context;
+        mUsbService = usbService;
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mUsbHandlerManager = new UsbHandlerManager(context);
     }
@@ -76,8 +74,7 @@
         synchronized (mSettingsByUser) {
             UsbUserSettingsManager settings = mSettingsByUser.get(userId);
             if (settings == null) {
-                settings = new UsbUserSettingsManager(mContext, UserHandle.of(userId),
-                        new UsbPermissionManager(mContext, UserHandle.of(userId)));
+                settings = new UsbUserSettingsManager(mContext, UserHandle.of(userId));
                 mSettingsByUser.put(userId, settings);
             }
             return settings;
@@ -164,46 +161,4 @@
 
         dump.end(token);
     }
-
-    /**
-     * Remove temporary access permission and broadcast that a device was removed.
-     *
-     * @param device The device that is removed
-     */
-    void usbDeviceRemoved(@NonNull UsbDevice device) {
-        synchronized (mSettingsByUser) {
-            for (int i = 0; i < mSettingsByUser.size(); i++) {
-                // clear temporary permissions for the device
-                mSettingsByUser.valueAt(i).removeDevicePermissions(device);
-            }
-        }
-
-        Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
-        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-        intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-
-        if (DEBUG) {
-            Slog.d(LOG_TAG, "usbDeviceRemoved, sending " + intent);
-        }
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
-    }
-
-    /**
-     * Remove temporary access permission and broadcast that a accessory was removed.
-     *
-     * @param accessory The accessory that is removed
-     */
-    void usbAccessoryRemoved(@NonNull UsbAccessory accessory) {
-        synchronized (mSettingsByUser) {
-            for (int i = 0; i < mSettingsByUser.size(); i++) {
-                // clear temporary permissions for the accessory
-                mSettingsByUser.valueAt(i).removeAccessoryPermissions(accessory);
-            }
-        }
-
-        Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
-        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-        intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
-    }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
new file mode 100644
index 0000000..ec7567c
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.UserHandle;
+import android.service.usb.UsbSettingsAccessoryPermissionProto;
+import android.service.usb.UsbSettingsDevicePermissionProto;
+import android.service.usb.UsbUserSettingsManagerProto;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
+import java.util.HashMap;
+
+/**
+ * UsbUserPermissionManager manages usb device or accessory access permissions.
+ *
+ * @hide
+ */
+class UsbUserPermissionManager {
+    private static final String LOG_TAG = UsbUserPermissionManager.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    @GuardedBy("mLock")
+    /** Temporary mapping USB device name to list of UIDs with permissions for the device*/
+    private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
+            new HashMap<>();
+    @GuardedBy("mLock")
+    /** Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory*/
+    private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
+            new HashMap<>();
+
+    private final Context mContext;
+    private final UserHandle mUser;
+    private final UsbUserSettingsManager mUsbUserSettingsManager;
+    private final boolean mDisablePermissionDialogs;
+
+    private final Object mLock = new Object();
+
+    UsbUserPermissionManager(@NonNull Context context, @NonNull UserHandle user,
+            @NonNull UsbUserSettingsManager usbUserSettingsManager) {
+        mContext = context;
+        mUser = user;
+        mUsbUserSettingsManager = usbUserSettingsManager;
+        mDisablePermissionDialogs = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+    }
+
+    /**
+     * Removes access permissions of all packages for the USB accessory.
+     *
+     * @param accessory to remove permissions for
+     */
+    void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
+        synchronized (mLock) {
+            mAccessoryPermissionMap.remove(accessory);
+        }
+    }
+
+    /**
+     * Removes access permissions of all packages for the USB device.
+     *
+     * @param device to remove permissions for
+     */
+    void removeDevicePermissions(@NonNull UsbDevice device) {
+        synchronized (mLock) {
+            mDevicePermissionMap.remove(device.getDeviceName());
+        }
+    }
+
+    /**
+     * Grants permission for USB device without showing system dialog for package with uid.
+     *
+     * @param device to grant permission for
+     * @param uid to grant permission for
+     */
+    void grantDevicePermission(@NonNull UsbDevice device, int uid) {
+        synchronized (mLock) {
+            String deviceName = device.getDeviceName();
+            SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+            if (uidList == null) {
+                uidList = new SparseBooleanArray(1);
+                mDevicePermissionMap.put(deviceName, uidList);
+            }
+            uidList.put(uid, true);
+        }
+    }
+
+    /**
+     * Grants permission for USB accessory without showing system dialog for package with uid.
+     *
+     * @param accessory to grant permission for
+     * @param uid to grant permission for
+     */
+    void grantAccessoryPermission(@NonNull UsbAccessory accessory, int uid) {
+        synchronized (mLock) {
+            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+            if (uidList == null) {
+                uidList = new SparseBooleanArray(1);
+                mAccessoryPermissionMap.put(accessory, uidList);
+            }
+            uidList.put(uid, true);
+        }
+    }
+
+    /**
+     * Returns true if package with uid has permission to access the device.
+     *
+     * @param device to check permission for
+     * @param uid to check permission for
+     * @return {@code true} if package with uid has permission
+     */
+    boolean hasPermission(@NonNull UsbDevice device, int uid) {
+        synchronized (mLock) {
+            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+                return true;
+            }
+            SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
+            if (uidList == null) {
+                return false;
+            }
+            return uidList.get(uid);
+        }
+    }
+
+    /**
+     * Returns true if caller has permission to access the accessory.
+     *
+     * @param accessory to check permission for
+     * @param uid to check permission for
+     * @return {@code true} if caller has permssion
+     */
+    boolean hasPermission(@NonNull UsbAccessory accessory, int uid) {
+        synchronized (mLock) {
+            if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+                return true;
+            }
+            SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+            if (uidList == null) {
+                return false;
+            }
+            return uidList.get(uid);
+        }
+    }
+
+    boolean hasPermission(UsbDevice device, String packageName, int uid) {
+        if (isCameraDevicePresent(device)) {
+            if (!isCameraPermissionGranted(packageName, uid)) {
+                return false;
+            }
+        }
+
+        return hasPermission(device, uid);
+    }
+
+    /**
+     * Creates UI dialog to request permission for the given package to access the device
+     * or accessory.
+     *
+     * @param device The USB device attached
+     * @param accessory The USB accessory attached
+     * @param canBeDefault Whether the calling pacakge can set as default handler
+     * of the USB device or accessory
+     * @param packageName The package name of the calling package
+     * @param uid The uid of the calling package
+     * @param userContext The context to start the UI dialog
+     * @param pi PendingIntent for returning result
+     */
+    void requestPermissionDialog(@Nullable UsbDevice device,
+                                 @Nullable UsbAccessory accessory,
+                                 boolean canBeDefault,
+                                 @NonNull String packageName,
+                                 int uid,
+                                 @NonNull Context userContext,
+                                 @NonNull PendingIntent pi) {
+        long identity = Binder.clearCallingIdentity();
+        Intent intent = new Intent();
+        if (device != null) {
+            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+        } else {
+            intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+        }
+        intent.putExtra(Intent.EXTRA_INTENT, pi);
+        intent.putExtra(Intent.EXTRA_UID, uid);
+        intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
+        intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
+        intent.setClassName("com.android.systemui",
+                "com.android.systemui.usb.UsbPermissionActivity");
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        try {
+            userContext.startActivityAsUser(intent, mUser);
+        } catch (ActivityNotFoundException e) {
+            Slog.e(LOG_TAG, "unable to start UsbPermissionActivity");
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    void dump(@NonNull DualDumpOutputStream dump) {
+        synchronized (mLock) {
+            for (String deviceName : mDevicePermissionMap.keySet()) {
+                long devicePermissionToken = dump.start("device_permissions",
+                        UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
+
+                dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
+
+                SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+                int count = uidList.size();
+                for (int i = 0; i < count; i++) {
+                    dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
+                }
+
+                dump.end(devicePermissionToken);
+            }
+
+            for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
+                long accessoryPermissionToken = dump.start("accessory_permissions",
+                        UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
+
+                dump.write("accessory_description",
+                        UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
+                        accessory.getDescription());
+
+                SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+                int count = uidList.size();
+                for (int i = 0; i < count; i++) {
+                    dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
+                }
+
+                dump.end(accessoryPermissionToken);
+            }
+        }
+    }
+
+    /**
+     * Check for camera permission of the calling process.
+     *
+     * @param packageName Package name of the caller.
+     * @param uid Linux uid of the calling process.
+     *
+     * @return True in case camera permission is available, False otherwise.
+     */
+    private boolean isCameraPermissionGranted(String packageName, int uid) {
+        int targetSdkVersion = android.os.Build.VERSION_CODES.P;
+        try {
+            ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
+            // compare uid with packageName to foil apps pretending to be someone else
+            if (aInfo.uid != uid) {
+                Slog.i(LOG_TAG, "Package " + packageName + " does not match caller's uid " + uid);
+                return false;
+            }
+            targetSdkVersion = aInfo.targetSdkVersion;
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.i(LOG_TAG, "Package not found, likely due to invalid package name!");
+            return false;
+        }
+
+        if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) {
+            int allowed = mContext.checkCallingPermission(android.Manifest.permission.CAMERA);
+            if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) {
+                Slog.i(LOG_TAG, "Camera permission required for USB video class devices");
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public void checkPermission(UsbDevice device, String packageName, int uid) {
+        if (!hasPermission(device, packageName, uid)) {
+            throw new SecurityException("User has not given " + uid + "/" + packageName
+                    + " permission to access device " + device.getDeviceName());
+        }
+    }
+
+    public void checkPermission(UsbAccessory accessory, int uid) {
+        if (!hasPermission(accessory, uid)) {
+            throw new SecurityException("User has not given " + uid + " permission to accessory "
+                    + accessory);
+        }
+    }
+
+    private void requestPermissionDialog(@Nullable UsbDevice device,
+            @Nullable UsbAccessory accessory,
+            boolean canBeDefault,
+            String packageName,
+            PendingIntent pi,
+            int uid) {
+        // compare uid with packageName to foil apps pretending to be someone else
+        try {
+            ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
+            if (aInfo.uid != uid) {
+                throw new IllegalArgumentException("package " + packageName
+                        + " does not match caller's uid " + uid);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalArgumentException("package " + packageName + " not found");
+        }
+
+        requestPermissionDialog(device, accessory, canBeDefault, packageName, uid, mContext, pi);
+    }
+
+    public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) {
+        Intent intent = new Intent();
+
+        // respond immediately if permission has already been granted
+        if (hasPermission(device, packageName, uid)) {
+            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+            try {
+                pi.send(mContext, 0, intent);
+            } catch (PendingIntent.CanceledException e) {
+                if (DEBUG) Slog.d(LOG_TAG, "requestPermission PendingIntent was cancelled");
+            }
+            return;
+        }
+        if (isCameraDevicePresent(device)) {
+            if (!isCameraPermissionGranted(packageName, uid)) {
+                intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+                intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
+                try {
+                    pi.send(mContext, 0, intent);
+                } catch (PendingIntent.CanceledException e) {
+                    if (DEBUG) Slog.d(LOG_TAG, "requestPermission PendingIntent was cancelled");
+                }
+                return;
+            }
+        }
+
+        requestPermissionDialog(device, null,
+                mUsbUserSettingsManager.canBeDefault(device, packageName), packageName, pi, uid);
+    }
+
+    public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi,
+            int uid) {
+        // respond immediately if permission has already been granted
+        if (hasPermission(accessory, uid)) {
+            Intent intent = new Intent();
+            intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+            try {
+                pi.send(mContext, 0, intent);
+            } catch (PendingIntent.CanceledException e) {
+                if (DEBUG) Slog.d(LOG_TAG, "requestPermission PendingIntent was cancelled");
+            }
+            return;
+        }
+
+        requestPermissionDialog(null, accessory,
+                mUsbUserSettingsManager.canBeDefault(accessory, packageName), packageName, pi, uid);
+    }
+
+    /**
+     * Check whether a particular device or any of its interfaces
+     * is of class VIDEO.
+     *
+     * @param device The device that needs to get scanned
+     * @return True in case a VIDEO device or interface is present,
+     *         False otherwise.
+     */
+    private boolean isCameraDevicePresent(UsbDevice device) {
+        if (device.getDeviceClass() == UsbConstants.USB_CLASS_VIDEO) {
+            return true;
+        }
+
+        for (int i = 0; i < device.getInterfaceCount(); i++) {
+            UsbInterface iface = device.getInterface(i);
+            if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_VIDEO) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
index 84add88..c2b8d01 100644
--- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
@@ -21,13 +21,10 @@
 import static com.android.server.usb.UsbProfileGroupSettingsManager.getDeviceFilters;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -36,9 +33,7 @@
 import android.hardware.usb.AccessoryFilter;
 import android.hardware.usb.DeviceFilter;
 import android.hardware.usb.UsbAccessory;
-import android.hardware.usb.UsbConstants;
 import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbInterface;
 import android.hardware.usb.UsbManager;
 import android.os.UserHandle;
 import android.service.usb.UsbAccessoryAttachedActivities;
@@ -62,12 +57,10 @@
 
     private final Context mUserContext;
     private final PackageManager mPackageManager;
-    private final UsbPermissionManager mUsbPermissionManager;
 
     private final Object mLock = new Object();
 
-    UsbUserSettingsManager(Context context, UserHandle user,
-            @NonNull UsbPermissionManager usbPermissionManager) {
+    UsbUserSettingsManager(Context context, UserHandle user) {
         if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
 
         try {
@@ -79,189 +72,6 @@
         mPackageManager = mUserContext.getPackageManager();
 
         mUser = user;
-        mUsbPermissionManager = usbPermissionManager;
-    }
-
-    /**
-     * Remove all access permission for a device.
-     *
-     * @param device The device the permissions are for
-     */
-    void removeDevicePermissions(@NonNull UsbDevice device) {
-        mUsbPermissionManager.removeDevicePermissions(device);
-    }
-
-    /**
-     * Remove all access permission for a accessory.
-     *
-     * @param accessory The accessory the permissions are for
-     */
-    void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
-        mUsbPermissionManager.removeAccessoryPermissions(accessory);
-    }
-
-    /**
-     * Check whether a particular device or any of its interfaces
-     * is of class VIDEO.
-     *
-     * @param device The device that needs to get scanned
-     * @return True in case a VIDEO device or interface is present,
-     *         False otherwise.
-     */
-    private boolean isCameraDevicePresent(UsbDevice device) {
-        if (device.getDeviceClass() == UsbConstants.USB_CLASS_VIDEO) {
-            return true;
-        }
-
-        for (int i = 0; i < device.getInterfaceCount(); i++) {
-            UsbInterface iface = device.getInterface(i);
-            if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_VIDEO) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Check for camera permission of the calling process.
-     *
-     * @param packageName Package name of the caller.
-     * @param uid Linux uid of the calling process.
-     *
-     * @return True in case camera permission is available, False otherwise.
-     */
-    private boolean isCameraPermissionGranted(String packageName, int uid) {
-        int targetSdkVersion = android.os.Build.VERSION_CODES.P;
-        try {
-            ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
-            // compare uid with packageName to foil apps pretending to be someone else
-            if (aInfo.uid != uid) {
-                Slog.i(TAG, "Package " + packageName + " does not match caller's uid " + uid);
-                return false;
-            }
-            targetSdkVersion = aInfo.targetSdkVersion;
-        } catch (PackageManager.NameNotFoundException e) {
-            Slog.i(TAG, "Package not found, likely due to invalid package name!");
-            return false;
-        }
-
-        if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) {
-            int allowed = mUserContext.checkCallingPermission(android.Manifest.permission.CAMERA);
-            if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) {
-                Slog.i(TAG, "Camera permission required for USB video class devices");
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    public boolean hasPermission(UsbDevice device, String packageName, int uid) {
-        if (isCameraDevicePresent(device)) {
-            if (!isCameraPermissionGranted(packageName, uid)) {
-                return false;
-            }
-        }
-
-        return mUsbPermissionManager.hasPermission(device, uid);
-    }
-
-    public boolean hasPermission(UsbAccessory accessory, int uid) {
-        return mUsbPermissionManager.hasPermission(accessory, uid);
-    }
-
-    public void checkPermission(UsbDevice device, String packageName, int uid) {
-        if (!hasPermission(device, packageName, uid)) {
-            throw new SecurityException("User has not given " + uid + "/" + packageName
-                    + " permission to access device " + device.getDeviceName());
-        }
-    }
-
-    public void checkPermission(UsbAccessory accessory, int uid) {
-        if (!hasPermission(accessory, uid)) {
-            throw new SecurityException("User has not given " + uid + " permission to accessory "
-                    + accessory);
-        }
-    }
-
-    private void requestPermissionDialog(@Nullable UsbDevice device,
-                                         @Nullable UsbAccessory accessory,
-                                         boolean canBeDefault,
-                                         String packageName,
-                                         PendingIntent pi,
-                                         int uid) {
-        // compare uid with packageName to foil apps pretending to be someone else
-        try {
-            ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
-            if (aInfo.uid != uid) {
-                throw new IllegalArgumentException("package " + packageName +
-                        " does not match caller's uid " + uid);
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new IllegalArgumentException("package " + packageName + " not found");
-        }
-
-        mUsbPermissionManager.requestPermissionDialog(device,
-                accessory, canBeDefault, packageName, uid, mUserContext, pi);
-    }
-
-    public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) {
-        Intent intent = new Intent();
-
-        // respond immediately if permission has already been granted
-        if (hasPermission(device, packageName, uid)) {
-            intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
-            try {
-                pi.send(mUserContext, 0, intent);
-            } catch (PendingIntent.CanceledException e) {
-                if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
-            }
-            return;
-        }
-        if (isCameraDevicePresent(device)) {
-            if (!isCameraPermissionGranted(packageName, uid)) {
-                intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-                intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
-                try {
-                    pi.send(mUserContext, 0, intent);
-                } catch (PendingIntent.CanceledException e) {
-                    if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
-                }
-                return;
-            }
-        }
-
-        requestPermissionDialog(device, null, canBeDefault(device, packageName), packageName, pi,
-                uid);
-    }
-
-    public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi,
-            int uid) {
-        // respond immediately if permission has already been granted
-        if (hasPermission(accessory, uid)) {
-            Intent intent = new Intent();
-            intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
-            intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
-            try {
-                pi.send(mUserContext, 0, intent);
-            } catch (PendingIntent.CanceledException e) {
-                if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
-            }
-            return;
-        }
-
-        requestPermissionDialog(null, accessory, canBeDefault(accessory, packageName), packageName,
-                pi, uid);
-    }
-
-    public void grantDevicePermission(UsbDevice device, int uid) {
-        mUsbPermissionManager.grantDevicePermission(device, uid);
-    }
-
-    public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
-        mUsbPermissionManager.grantAccessoryPermission(accessory, uid);
     }
 
     /**
@@ -285,7 +95,7 @@
      *
      * @return {@code true} if the app can be default
      */
-    private boolean canBeDefault(@NonNull UsbDevice device, String packageName) {
+    boolean canBeDefault(@NonNull UsbDevice device, String packageName) {
         ActivityInfo[] activities = getPackageActivities(packageName);
         if (activities != null) {
             int numActivities = activities.length;
@@ -327,7 +137,7 @@
      *
      * @return {@code true} if the app can be default
      */
-    private boolean canBeDefault(@NonNull UsbAccessory accessory, String packageName) {
+    boolean canBeDefault(@NonNull UsbAccessory accessory, String packageName) {
         ActivityInfo[] activities = getPackageActivities(packageName);
         if (activities != null) {
             int numActivities = activities.length;
@@ -377,8 +187,6 @@
         synchronized (mLock) {
             dump.write("user_id", UsbUserSettingsManagerProto.USER_ID, mUser.getIdentifier());
 
-            mUsbPermissionManager.dump(dump);
-
             List<ResolveInfo> deviceAttachedActivities = queryIntentActivities(
                     new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED));
             int numDeviceAttachedActivities = deviceAttachedActivities.size();
diff --git a/services/usb/java/com/android/server/usb/descriptors/ByteStream.java b/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
index 1e823b6..56dc3e0 100644
--- a/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
+++ b/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
@@ -185,12 +185,14 @@
             // Positive offsets only
             throw new IllegalArgumentException();
         }
-        // do arithmetic and comparison in long to ovoid potention integer overflow
+        // do arithmetic and comparison in long to avoid potential integer overflow
         long longNewIndex = (long) mIndex + (long) numBytes;
-        if (longNewIndex < (long) mBytes.length) {
+        if (longNewIndex <= (long) mBytes.length) {
             mReadCount += numBytes;
             mIndex += numBytes;
         } else {
+            // Position the stream to the end so available() will return 0
+            mIndex = mBytes.length;
             throw new IndexOutOfBoundsException();
         }
     }
@@ -210,6 +212,7 @@
             mReadCount -= numBytes;
             mIndex -= numBytes;
         } else {
+            mIndex = 0;
             throw new IndexOutOfBoundsException();
         }
     }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
index 7ebccf3..0535d71 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
@@ -45,7 +45,6 @@
     @Override
     public int parseRawDescriptors(ByteStream stream) {
         mSubtype = stream.getByte();
-
         return mLength;
     }
 
@@ -55,12 +54,21 @@
         int subClass = interfaceDesc.getUsbSubclass();
         switch (subClass) {
             case AUDIO_AUDIOCONTROL:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "---> AUDIO_AUDIOCONTROL");
+                }
                 return new UsbACAudioControlEndpoint(length, type, subClass);
 
             case AUDIO_AUDIOSTREAMING:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "---> AUDIO_AUDIOSTREAMING");
+                }
                 return new UsbACAudioStreamEndpoint(length, type, subClass);
 
             case AUDIO_MIDISTREAMING:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "---> AUDIO_MIDISTREAMING");
+                }
                 return new UsbACMidiEndpoint(length, type, subClass);
 
             default:
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
index 38c12a1..82fbfb8 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
@@ -100,8 +100,14 @@
         switch (subtype) {
             case ACI_HEADER:
             {
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_HEADER");
+                }
                 int acInterfaceSpec = stream.unpackUsbShort();
                 parser.setACInterfaceSpec(acInterfaceSpec);
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+                }
                 if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                     return new Usb20ACHeader(length, type, subtype, subClass, acInterfaceSpec);
                 } else {
@@ -111,7 +117,13 @@
 
             case ACI_INPUT_TERMINAL:
             {
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_INPUT_TERMINAL");
+                }
                 int acInterfaceSpec = parser.getACInterfaceSpec();
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+                }
                 if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                     return new Usb20ACInputTerminal(length, type, subtype, subClass);
                 } else {
@@ -121,7 +133,13 @@
 
             case ACI_OUTPUT_TERMINAL:
             {
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_OUTPUT_TERMINAL");
+                }
                 int acInterfaceSpec = parser.getACInterfaceSpec();
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+                }
                 if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                     return new Usb20ACOutputTerminal(length, type, subtype, subClass);
                 } else {
@@ -130,14 +148,26 @@
             }
 
             case ACI_SELECTOR_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_SELECTOR_UNIT");
+                }
                 return new UsbACSelectorUnit(length, type, subtype, subClass);
 
             case ACI_FEATURE_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_FEATURE_UNIT");
+                }
                 return new UsbACFeatureUnit(length, type, subtype, subClass);
 
             case ACI_MIXER_UNIT:
             {
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> ACI_MIXER_UNIT");
+                }
                 int acInterfaceSpec = parser.getACInterfaceSpec();
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+                }
                 if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
                     return new Usb20ACMixerUnit(length, type, subtype, subClass);
                 } else {
@@ -215,14 +245,23 @@
         int subClass = interfaceDesc.getUsbSubclass();
         switch (subClass) {
             case AUDIO_AUDIOCONTROL:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  AUDIO_AUDIOCONTROL");
+                }
                 return allocAudioControlDescriptor(
                         parser, stream, length, type, subtype, subClass);
 
             case AUDIO_AUDIOSTREAMING:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  AUDIO_AUDIOSTREAMING");
+                }
                 return allocAudioStreamingDescriptor(
                         parser, stream, length, type, subtype, subClass);
 
             case AUDIO_MIDISTREAMING:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, "  AUDIO_MIDISTREAMING");
+                }
                 return allocMidiStreamingDescriptor(length, type, subtype, subClass);
 
             default:
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
index 639aa4e..de2dd10 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
@@ -30,7 +30,6 @@
  */
 public final class UsbConfigDescriptor extends UsbDescriptor {
     private static final String TAG = "UsbConfigDescriptor";
-    private static final boolean DEBUG = false;
 
     private int mTotalLength;    // 2:2 Total length in bytes of data returned
     private byte mNumInterfaces; // 4:1 Number of Interfaces
@@ -79,14 +78,14 @@
     }
 
     UsbConfiguration toAndroid(UsbDescriptorParser parser) {
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "  toAndroid()");
         }
         String name = parser.getDescriptorString(mConfigIndex);
         UsbConfiguration config = new
                 UsbConfiguration(mConfigValue, name, mAttribs, mMaxPower);
         UsbInterface[] interfaces = new UsbInterface[mInterfaceDescriptors.size()];
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "    " + mInterfaceDescriptors.size() + " interfaces.");
         }
         for (int index = 0; index < mInterfaceDescriptors.size(); index++) {
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
index ff67667..44422a2 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
@@ -43,7 +43,7 @@
     protected int mHierarchyLevel;
 
     protected final int mLength;    // 0:1 bLength Number Size of the Descriptor in Bytes (18 bytes)
-                                    // we store this as an int because Java bytes are SIGNED.
+    // we store this as an int because Java bytes are SIGNED.
     protected final byte mType;     // 1:1 bDescriptorType Constant Device Descriptor (0x01)
 
     private byte[] mRawData;
@@ -52,11 +52,11 @@
     private static byte[] sStringBuffer = new byte[SIZE_STRINGBUFFER];
 
     // Status
-    public static final int STATUS_UNPARSED         = 0;
-    public static final int STATUS_PARSED_OK        = 1;
-    public static final int STATUS_PARSED_UNDERRUN  = 2;
-    public static final int STATUS_PARSED_OVERRUN   = 3;
-    public static final int STATUS_PARSE_EXCEPTION  = 4;
+    public static final int STATUS_UNPARSED = 0;
+    public static final int STATUS_PARSED_OK = 1;
+    public static final int STATUS_PARSED_UNDERRUN = 2;
+    public static final int STATUS_PARSED_OVERRUN = 3;
+    public static final int STATUS_PARSE_EXCEPTION = 4;
 
     private int mStatus = STATUS_UNPARSED;
 
@@ -78,53 +78,53 @@
     public static final byte DESCRIPTORTYPE_HID = 0x21;                // 33
     public static final byte DESCRIPTORTYPE_REPORT = 0x22;             // 34
     public static final byte DESCRIPTORTYPE_PHYSICAL = 0x23;           // 35
-    public static final byte DESCRIPTORTYPE_AUDIO_INTERFACE = 0x24;    // 36
-    public static final byte DESCRIPTORTYPE_AUDIO_ENDPOINT = 0x25;     // 37
+    public static final byte DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE = 0x24;    // 36
+    public static final byte DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT = 0x25;     // 37
     public static final byte DESCRIPTORTYPE_HUB = 0x29;                // 41
     public static final byte DESCRIPTORTYPE_SUPERSPEED_HUB = 0x2A;     // 42
     public static final byte DESCRIPTORTYPE_ENDPOINT_COMPANION = 0x30; // 48
 
     // Class IDs
-    public static final int CLASSID_DEVICE  =      0x00;
-    public static final int CLASSID_AUDIO =        0x01;
-    public static final int CLASSID_COM =          0x02;
-    public static final int CLASSID_HID =          0x03;
+    public static final int CLASSID_DEVICE = 0x00;
+    public static final int CLASSID_AUDIO = 0x01;
+    public static final int CLASSID_COM = 0x02;
+    public static final int CLASSID_HID = 0x03;
     // public static final int CLASSID_??? =       0x04;
-    public static final int CLASSID_PHYSICAL =     0x05;
-    public static final int CLASSID_IMAGE =        0x06;
-    public static final int CLASSID_PRINTER =      0x07;
-    public static final int CLASSID_STORAGE =      0x08;
-    public static final int CLASSID_HUB =          0x09;
-    public static final int CLASSID_CDC_CONTROL =  0x0A;
-    public static final int CLASSID_SMART_CARD =   0x0B;
+    public static final int CLASSID_PHYSICAL = 0x05;
+    public static final int CLASSID_IMAGE = 0x06;
+    public static final int CLASSID_PRINTER = 0x07;
+    public static final int CLASSID_STORAGE = 0x08;
+    public static final int CLASSID_HUB = 0x09;
+    public static final int CLASSID_CDC_CONTROL = 0x0A;
+    public static final int CLASSID_SMART_CARD = 0x0B;
     //public static final int CLASSID_??? =        0x0C;
-    public static final int CLASSID_SECURITY =     0x0D;
-    public static final int CLASSID_VIDEO =        0x0E;
-    public static final int CLASSID_HEALTHCARE =   0x0F;
-    public static final int CLASSID_AUDIOVIDEO =   0x10;
-    public static final int CLASSID_BILLBOARD =    0x11;
-    public static final int CLASSID_TYPECBRIDGE =  0x12;
-    public static final int CLASSID_DIAGNOSTIC =   0xDC;
-    public static final int CLASSID_WIRELESS =     0xE0;
-    public static final int CLASSID_MISC =         0xEF;
-    public static final int CLASSID_APPSPECIFIC =  0xFE;
+    public static final int CLASSID_SECURITY = 0x0D;
+    public static final int CLASSID_VIDEO = 0x0E;
+    public static final int CLASSID_HEALTHCARE = 0x0F;
+    public static final int CLASSID_AUDIOVIDEO = 0x10;
+    public static final int CLASSID_BILLBOARD = 0x11;
+    public static final int CLASSID_TYPECBRIDGE = 0x12;
+    public static final int CLASSID_DIAGNOSTIC = 0xDC;
+    public static final int CLASSID_WIRELESS = 0xE0;
+    public static final int CLASSID_MISC = 0xEF;
+    public static final int CLASSID_APPSPECIFIC = 0xFE;
     public static final int CLASSID_VENDSPECIFIC = 0xFF;
 
     // Audio Subclass codes
-    public static final int AUDIO_SUBCLASS_UNDEFINED   = 0x00;
-    public static final int AUDIO_AUDIOCONTROL         = 0x01;
-    public static final int AUDIO_AUDIOSTREAMING       = 0x02;
-    public static final int AUDIO_MIDISTREAMING        = 0x03;
+    public static final int AUDIO_SUBCLASS_UNDEFINED = 0x00;
+    public static final int AUDIO_AUDIOCONTROL = 0x01;
+    public static final int AUDIO_AUDIOSTREAMING = 0x02;
+    public static final int AUDIO_MIDISTREAMING = 0x03;
 
     // Request IDs
-    public static final int REQUEST_GET_STATUS         = 0x00;
-    public static final int REQUEST_CLEAR_FEATURE      = 0x01;
-    public static final int REQUEST_SET_FEATURE        = 0x03;
-    public static final int REQUEST_GET_ADDRESS        = 0x05;
-    public static final int REQUEST_GET_DESCRIPTOR     = 0x06;
-    public static final int REQUEST_SET_DESCRIPTOR     = 0x07;
-    public static final int REQUEST_GET_CONFIGURATION  = 0x08;
-    public static final int REQUEST_SET_CONFIGURATION  = 0x09;
+    public static final int REQUEST_GET_STATUS = 0x00;
+    public static final int REQUEST_CLEAR_FEATURE = 0x01;
+    public static final int REQUEST_SET_FEATURE = 0x03;
+    public static final int REQUEST_GET_ADDRESS = 0x05;
+    public static final int REQUEST_GET_DESCRIPTOR = 0x06;
+    public static final int REQUEST_SET_DESCRIPTOR = 0x07;
+    public static final int REQUEST_GET_CONFIGURATION = 0x08;
+    public static final int REQUEST_SET_CONFIGURATION = 0x09;
 
     // USB control transfer timeout
     public static final int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
@@ -163,7 +163,6 @@
     public int getOverUnderRunCount() {
         return mOverUnderRunCount;
     }
-
     public String getStatusString() {
         return sStatusStrings[mStatus];
     }
@@ -278,4 +277,24 @@
                 + " Len: " + getLength();
         canvas.writeParagraph(text, false);
     }
+
+    /*
+     * Logging Helpers
+     */
+    static String getDescriptorName(byte descriptorType, int descriptorLength) {
+        String name = UsbStrings.getDescriptorName(descriptorType);
+        if (name != null) {
+            return name;
+        } else {
+            return "Unknown Descriptor Type " + descriptorType
+                + " 0x" + Integer.toHexString(descriptorType)
+                + " length:" + descriptorLength;
+        }
+    }
+
+    static void logDescriptorName(byte descriptorType, int descriptorLength) {
+        if (UsbDescriptorParser.DEBUG) {
+            Log.d(TAG, "----> " + getDescriptorName(descriptorType, descriptorLength));
+        }
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index c021101..4f62d5a 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -26,7 +26,7 @@
  */
 public final class UsbDescriptorParser {
     private static final String TAG = "UsbDescriptorParser";
-    private static final boolean DEBUG = false;
+    public static final boolean DEBUG = false;
 
     private final String mDeviceAddr;
 
@@ -115,6 +115,8 @@
         int length = stream.getUnsignedByte();
         byte type = stream.getByte();
 
+        UsbDescriptor.logDescriptorName(type, length);
+
         UsbDescriptor descriptor = null;
         switch (type) {
             /*
@@ -174,14 +176,56 @@
                 break;
 
             /*
-             * Audio Class Specific
+             * Various Class Specific
              */
-            case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE:
-                descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+            case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE:
+                if (mCurInterfaceDescriptor != null) {
+                    switch (mCurInterfaceDescriptor.getUsbClass()) {
+                        case UsbDescriptor.CLASSID_AUDIO:
+                            descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+                            break;
+
+                        case UsbDescriptor.CLASSID_VIDEO:
+                            Log.d(TAG, "  UsbDescriptor.CLASSID_VIDEO subType:0x"
+                                    + Integer.toHexString(stream.getByte()));
+                            descriptor = UsbVCInterface.allocDescriptor(this, stream, length, type);
+                            break;
+
+                        case UsbDescriptor.CLASSID_AUDIOVIDEO:
+                            Log.d(TAG, "  UsbDescriptor.CLASSID_AUDIOVIDEO subType:0x"
+                                    + Integer.toHexString(stream.getByte()));
+                            break;
+
+                        default:
+                            Log.d(TAG, "  Unparsed Class-specific Interface:0x"
+                                    + Integer.toHexString(mCurInterfaceDescriptor.getUsbClass()));
+                            break;
+                    }
+                }
                 break;
 
-            case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT:
-                descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
+            case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT:
+                if (mCurInterfaceDescriptor != null) {
+                    switch (mCurInterfaceDescriptor.getUsbClass()) {
+                        case UsbDescriptor.CLASSID_AUDIO:
+                            descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
+                            break;
+                        case UsbDescriptor.CLASSID_VIDEO:
+                            Log.d(TAG, "UsbDescriptor.CLASSID_VIDEO subType:0x"
+                                    + Integer.toHexString(stream.getByte()));
+                            descriptor = UsbVCEndpoint.allocDescriptor(this, length, type);
+                            break;
+
+                        case UsbDescriptor.CLASSID_AUDIOVIDEO:
+                            Log.d(TAG, "UsbDescriptor.CLASSID_AUDIOVIDEO subType:0x"
+                                    + Integer.toHexString(stream.getByte()));
+                            break;
+                        default:
+                            Log.d(TAG, "  Unparsed Class-specific Endpoint:0x"
+                                    + Integer.toHexString(mCurInterfaceDescriptor.getUsbClass()));
+                            break;
+                    }
+                }
                 break;
 
             default:
@@ -190,8 +234,6 @@
 
         if (descriptor == null) {
             // Unknown Descriptor
-            Log.i(TAG, "Unknown Descriptor len: " + length + " type:0x"
-                    + Integer.toHexString(type));
             descriptor = new UsbUnknown(length, type);
         }
 
@@ -210,10 +252,6 @@
      * @hide
      */
     public void parseDescriptors(byte[] descriptors) {
-        if (DEBUG) {
-            Log.d(TAG, "parseDescriptors() - start");
-        }
-
         ByteStream stream = new ByteStream(descriptors);
         while (stream.available() > 0) {
             UsbDescriptor descriptor = null;
@@ -325,8 +363,8 @@
     public ArrayList<UsbDescriptor> getACInterfaceDescriptors(byte subtype, int subclass) {
         ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
         for (UsbDescriptor descriptor : mDescriptors) {
-            if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE) {
-                // ensure that this isn't an unrecognized DESCRIPTORTYPE_AUDIO_INTERFACE
+            if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE) {
+                // ensure that this isn't an unrecognized DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE
                 if (descriptor instanceof UsbACInterface) {
                     UsbACInterface acDescriptor = (UsbACInterface) descriptor;
                     if (acDescriptor.getSubtype() == subtype
@@ -334,8 +372,8 @@
                         list.add(descriptor);
                     }
                 } else {
-                    Log.w(TAG, "Unrecognized Audio Interface l: " + descriptor.getLength()
-                            + " t:0x" + Integer.toHexString(descriptor.getType()));
+                    Log.w(TAG, "Unrecognized Audio Interface len: " + descriptor.getLength()
+                            + " type:0x" + Integer.toHexString(descriptor.getType()));
                 }
             }
         }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
index f50b9cb..e6e10fe 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -31,7 +31,6 @@
  */
 public final class UsbDeviceDescriptor extends UsbDescriptor {
     private static final String TAG = "UsbDeviceDescriptor";
-    private static final boolean DEBUG = false;
 
     public static final int USBSPEC_1_0 = 0x0100;
     public static final int USBSPEC_1_1 = 0x0110;
@@ -136,19 +135,19 @@
      * @hide
      */
     public UsbDevice.Builder toAndroid(UsbDescriptorParser parser) {
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "toAndroid()");
         }
 
         String mfgName = getMfgString(parser);
         String prodName = getProductString(parser);
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "  mfgName:" + mfgName + " prodName:" + prodName);
         }
 
         String versionString = getDeviceReleaseString();
         String serialStr = getSerialString(parser);
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "  versionString:" + versionString + " serialStr:" + serialStr);
         }
 
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index 4da31ea..5eb0a2f 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -27,7 +27,6 @@
  */
 public class UsbEndpointDescriptor extends UsbDescriptor {
     private static final String TAG = "UsbEndpointDescriptor";
-    private static final boolean DEBUG = false;
 
     public static final int MASK_ENDPOINT_ADDRESS = 0b000000000001111;
     public static final int MASK_ENDPOINT_DIRECTION = (byte) 0b0000000010000000;
@@ -110,7 +109,7 @@
     }
 
     /* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) {
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "toAndroid() type:"
                     + Integer.toHexString(mAttributes & MASK_ATTRIBS_TRANSTYPE)
                     + " sync:" + Integer.toHexString(mAttributes & MASK_ATTRIBS_SYNCTYPE)
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index 632e3dc..1dc6069 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -31,7 +31,6 @@
  */
 public class UsbInterfaceDescriptor extends UsbDescriptor {
     private static final String TAG = "UsbInterfaceDescriptor";
-    private static final boolean DEBUG = false;
 
     protected int mInterfaceNumber;   // 2:1 Number of Interface
     protected byte mAlternateSetting; // 3:1 Value used to select alternative setting
@@ -95,7 +94,7 @@
     }
 
     UsbInterface toAndroid(UsbDescriptorParser parser) {
-        if (DEBUG) {
+        if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "toAndroid() class:" + Integer.toHexString(mUsbClass)
                     + " subclass:" + Integer.toHexString(mUsbSubclass)
                     + " " + mEndpointDescriptors.size() + " endpoints.");
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
new file mode 100644
index 0000000..39fbc0d
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+/**
+ * @hide
+ * A video class-specific Endpoint
+ * see
+ */
+abstract class UsbVCEndpoint extends UsbDescriptor {
+    private static final String TAG = "UsbVCEndpoint";
+
+    UsbVCEndpoint(int length, byte type, int subclass) {
+        super(length, type);
+        // mSubclass = subclass;
+    }
+
+    public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
+                                                int length, byte type) {
+        UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
+        int subClass = interfaceDesc.getUsbSubclass();
+        switch (subClass) {
+//            case AUDIO_AUDIOCONTROL:
+//                if (UsbDescriptorParser.DEBUG) {
+//                    Log.i(TAG, "---> AUDIO_AUDIOCONTROL");
+//                }
+//                return new UsbACAudioControlEndpoint(length, type, subClass);
+//
+//            case AUDIO_AUDIOSTREAMING:
+//                if (UsbDescriptorParser.DEBUG) {
+//                    Log.i(TAG, "---> AUDIO_AUDIOSTREAMING");
+//                }
+//                return new UsbACAudioStreamEndpoint(length, type, subClass);
+//
+//            case AUDIO_MIDISTREAMING:
+//                if (UsbDescriptorParser.DEBUG) {
+//                    Log.i(TAG, "---> AUDIO_MIDISTREAMING");
+//                }
+//                return new UsbACMidiEndpoint(length, type, subClass);
+
+            default:
+                Log.w(TAG, "Unknown Video Class Endpoint id:0x" + Integer.toHexString(subClass));
+                return null;
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
new file mode 100644
index 0000000..c9eb1ec
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+/**
+ * @hide
+ * A video class-specific Interface.
+ * see USB_Video_Class_1.1.pdf, section 3.7.2
+ */
+public abstract class UsbVCInterface extends UsbDescriptor {
+    private static final String TAG = "UsbVCInterface";
+
+    // Class-specific Video Subtypes
+    public static final byte VCI_UNDEFINED          = 0x00;
+    public static final byte VCI_VEADER             = 0x01;
+    public static final byte VCI_INPUT_TERMINAL     = 0x02;
+    public static final byte VCI_VOUTPUT_TERMINAL   = 0x03;
+    public static final byte VCI_SELECTOR_UNIT      = 0x04;
+    public static final byte VCI_VROCESSING_UNIT    = 0x05;
+    public static final byte VCI_VEXTENSION_UNIT    = 0x06;
+
+   // See “Universal Serial Bus Device Class Definition for Video
+    protected final byte mSubtype;  // 2:1 HEADER descriptor subtype
+    protected final int mSubclass;  // from the mSubclass member of the
+    // "enclosing" Interface Descriptor
+
+    public UsbVCInterface(int length, byte type, byte subtype, int subclass) {
+        super(length, type);
+        mSubtype = subtype;
+        mSubclass = subclass;
+    }
+
+    /**
+     * Allocates an audio class interface subtype based on subtype and subclass.
+     */
+    public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser, ByteStream stream,
+                                                int length, byte type) {
+        byte subtype = stream.getByte();
+        UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
+        int subClass = interfaceDesc.getUsbSubclass();
+        if (UsbDescriptorParser.DEBUG) {
+            Log.d(TAG, "  Video Class-specific Interface subClass:0x"
+                    + Integer.toHexString(subClass));
+        }
+        switch (subClass) {
+            // TODO - Create descriptor classes and parse these...
+            case VCI_UNDEFINED:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_UNDEFINED");
+                }
+                break;
+
+            case VCI_VEADER:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_VEADER");
+                }
+                break;
+
+            case VCI_INPUT_TERMINAL:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_INPUT_TERMINAL");
+                }
+                break;
+
+            case VCI_VOUTPUT_TERMINAL:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_VOUTPUT_TERMINAL");
+                }
+                break;
+
+            case VCI_SELECTOR_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_SELECTOR_UNIT");
+                }
+                break;
+
+            case VCI_VROCESSING_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_VROCESSING_UNIT");
+                }
+                break;
+
+            case VCI_VEXTENSION_UNIT:
+                if (UsbDescriptorParser.DEBUG) {
+                    Log.d(TAG, " ---> VCI_VEXTENSION_UNIT");
+                }
+                break;
+
+            default:
+                Log.w(TAG, "Unknown Video Class Interface Subclass: 0x"
+                        + Integer.toHexString(subClass));
+                return null;
+        }
+
+        return null;
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java b/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
index fb4576a..918ba2cc 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
@@ -56,9 +56,10 @@
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_HID, "HID");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_REPORT, "Report");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_PHYSICAL, "Physical");
-        sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE,
-                "Audio Class Interface");
-        sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT, "Audio Class Endpoint");
+        sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE,
+                "Class-specific Interface");
+        sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT,
+                "Class-specific Endpoint");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_HUB, "Hub");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_SUPERSPEED_HUB, "Superspeed Hub");
         sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_ENDPOINT_COMPANION,
diff --git a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java
index 1aa30fa..72fa897 100644
--- a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java
+++ b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java
@@ -126,11 +126,13 @@
                 //
                 // Audio Class Descriptors
                 //
-                case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE:
-                    addACInterface((UsbACInterface) descriptor);
+                case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE:
+                    //TODO: This needs to be parsed out to Audio/Video...
+                    // addACInterface((UsbACInterface) descriptor);
                     break;
 
-                case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT:
+                case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT:
+                    //TODO: This needs to be parsed out to Audio/Video...
                     break;
             }
         }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index b2ac549..6132cc2 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -52,8 +52,9 @@
 import android.os.RemoteCallback;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.os.UserHandle;
-import android.os.UserManager;
+import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.service.voice.IVoiceInteractionService;
 import android.service.voice.IVoiceInteractionSession;
@@ -95,14 +96,14 @@
  */
 public class VoiceInteractionManagerService extends SystemService {
     static final String TAG = "VoiceInteractionManagerService";
-    static final boolean DEBUG = true; // TODO(b/133242016) STOPSHIP: change to false before R ships
+    static final boolean DEBUG = false;
 
     final Context mContext;
     final ContentResolver mResolver;
     final DatabaseHelper mDbHelper;
     final ActivityManagerInternal mAmInternal;
     final ActivityTaskManagerInternal mAtmInternal;
-    final UserManager mUserManager;
+    final UserManagerInternal mUserManagerInternal;
     final ArraySet<Integer> mLoadedKeyphraseIds = new ArraySet<>();
     ShortcutServiceInternal mShortcutServiceInternal;
     SoundTriggerInternal mSoundTriggerInternal;
@@ -120,8 +121,8 @@
                 LocalServices.getService(ActivityManagerInternal.class));
         mAtmInternal = Preconditions.checkNotNull(
                 LocalServices.getService(ActivityTaskManagerInternal.class));
-        mUserManager = Preconditions.checkNotNull(
-                context.getSystemService(UserManager.class));
+        mUserManagerInternal = Preconditions.checkNotNull(
+                LocalServices.getService(UserManagerInternal.class));
 
         PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
                 PermissionManagerServiceInternal.class);
@@ -157,37 +158,30 @@
     }
 
     @Override
-    public void onStartUser(@NonNull UserInfo userInfo) {
-        if (DEBUG) Slog.d(TAG, "onStartUser(" + userInfo + ")");
+    public boolean isSupported(UserInfo userInfo) {
+        return userInfo.isFull();
+    }
 
-        if (!userInfo.isFull()) {
-            if (DEBUG) Slog.d(TAG,  "***** skipping on non-full user " + userInfo);
-            return;
-        }
+    @Override
+    public void onStartUser(@NonNull UserInfo userInfo) {
+        if (DEBUG_USER) Slog.d(TAG, "onStartUser(" + userInfo + ")");
+
         mServiceStub.initForUser(userInfo.id);
     }
 
     @Override
     public void onUnlockUser(@NonNull UserInfo userInfo) {
-        if (DEBUG) Slog.d(TAG, "onUnlockUser(" + userInfo + ")");
+        if (DEBUG_USER) Slog.d(TAG, "onUnlockUser(" + userInfo + ")");
 
-        if (!userInfo.isFull()) {
-            if (DEBUG) Slog.d(TAG,  "***** skipping on non-full user " + userInfo);
-            return;
-        }
         mServiceStub.initForUser(userInfo.id);
         mServiceStub.switchImplementationIfNeeded(false);
     }
 
     @Override
-    public void onSwitchUser(@NonNull UserInfo userInfo) {
-        if (DEBUG) Slog.d(TAG, "onSwitchUser(" + userInfo + ")");
+    public void onSwitchUser(@NonNull UserInfo from, @NonNull UserInfo to) {
+        if (DEBUG_USER) Slog.d(TAG, "onSwitchUser(" + from + " > " + to + ")");
 
-        if (!userInfo.isFull()) {
-            if (DEBUG) Slog.d(TAG,  "***** skipping on non-full user " + userInfo);
-            return;
-        }
-        mServiceStub.switchUser(userInfo.id);
+        mServiceStub.switchUser(to.id);
     }
 
     class LocalService extends VoiceInteractionManagerInternal {
@@ -225,6 +219,7 @@
         private boolean mSafeMode;
         private int mCurUser;
         private boolean mCurUserUnlocked;
+        private boolean mCurUserSupported;
         private final boolean mEnableService;
 
         VoiceInteractionManagerServiceStub() {
@@ -292,9 +287,9 @@
 
         public void initForUser(int userHandle) {
             final TimingsTraceAndSlog t;
-            if (DEBUG) {
-                t = TimingsTraceAndSlog.newAsyncLog();
-                t.traceBegin("VoiceInteractionSvc.initForUser(" + userHandle + ")");
+            if (DEBUG_USER) {
+                t = new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+                t.traceBegin("initForUser(" + userHandle + ")");
             } else {
                 t = null;
             }
@@ -439,15 +434,21 @@
             new SettingsObserver(UiThread.getHandler());
 
             synchronized (this) {
-                mCurUser = ActivityManager.getCurrentUser();
+                setCurrentUserLocked(ActivityManager.getCurrentUser());
                 switchImplementationIfNeededLocked(false);
             }
         }
 
-        public void switchUser(int userHandle) {
+        private void setCurrentUserLocked(@UserIdInt int userHandle) {
+            mCurUser = userHandle;
+            final UserInfo userInfo = mUserManagerInternal.getUserInfo(mCurUser);
+            mCurUserSupported = isSupported(userInfo);
+        }
+
+        public void switchUser(@UserIdInt int userHandle) {
             FgThread.getHandler().post(() -> {
                 synchronized (this) {
-                    mCurUser = userHandle;
+                    setCurrentUserLocked(userHandle);
                     mCurUserUnlocked = false;
                     switchImplementationIfNeededLocked(false);
                 }
@@ -461,10 +462,22 @@
         }
 
         void switchImplementationIfNeededLocked(boolean force) {
+            if (!mCurUserSupported) {
+                if (DEBUG_USER) {
+                    Slog.d(TAG, "switchImplementationIfNeeded(): skipping on unsuported user "
+                            + mCurUser);
+                }
+                if (mImpl != null) {
+                    mImpl.shutdownLocked();
+                    setImplLocked(null);
+                }
+                return;
+            }
+
             final TimingsTraceAndSlog t;
-            if (DEBUG) {
-                t = TimingsTraceAndSlog.newAsyncLog();
-                t.traceBegin("VoiceInteractionSvc.switchImplementation(" + mCurUser + ")");
+            if (DEBUG_USER) {
+                t = new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+                t.traceBegin("switchImplementation(" + mCurUser + ")");
             } else {
                 t = null;
             }
@@ -494,7 +507,7 @@
 
                 final boolean hasComponent = serviceComponent != null && serviceInfo != null;
 
-                if (mUserManager.isUserUnlockingOrUnlocked(mCurUser)) {
+                if (mUserManagerInternal.isUserUnlockingOrUnlocked(mCurUser)) {
                     if (hasComponent) {
                         mShortcutServiceInternal.setShortcutHostPackage(TAG,
                                 serviceComponent.getPackageName(), mCurUser);
@@ -1275,6 +1288,9 @@
             synchronized (this) {
                 pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)");
                 pw.println("  mEnableService: " + mEnableService);
+                pw.println("  mCurUser: " + mCurUser);
+                pw.println("  mCurUserUnlocked: " + mCurUserUnlocked);
+                pw.println("  mCurUserSupported: " + mCurUserSupported);
                 if (mImpl == null) {
                     pw.println("  (No active implementation)");
                     return;
@@ -1330,9 +1346,12 @@
 
             RoleObserver(@NonNull @CallbackExecutor Executor executor) {
                 mRm.addOnRoleHoldersChangedListenerAsUser(executor, this, UserHandle.ALL);
-                UserHandle currentUser = UserHandle.of(LocalServices.getService(
-                        ActivityManagerInternal.class).getCurrentUserId());
-                onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT, currentUser);
+                // Sync only if assistant role has been initialized.
+                if (mRm.isRoleAvailable(RoleManager.ROLE_ASSISTANT)) {
+                    UserHandle currentUser = UserHandle.of(LocalServices.getService(
+                            ActivityManagerInternal.class).getCurrentUserId());
+                    onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT, currentUser);
+                }
             }
 
             private @NonNull String getDefaultRecognizer(@NonNull UserHandle user) {
diff --git a/startop/scripts/app_startup/lib/adb_utils.py b/startop/scripts/app_startup/lib/adb_utils.py
index 1c60a17..e56a968 100644
--- a/startop/scripts/app_startup/lib/adb_utils.py
+++ b/startop/scripts/app_startup/lib/adb_utils.py
@@ -42,6 +42,8 @@
 def vm_drop_cache():
   """Free pagecache and slab object."""
   cmd_utils.run_adb_shell_command('echo 3 > /proc/sys/vm/drop_caches')
+  # Sleep a little bit to provide enougth time for cache cleanup.
+  time.sleep(2)
 
 def root():
   """Roots adb and successive adb commands will run under root."""
diff --git a/startop/scripts/iorap/compiler.py b/startop/scripts/iorap/compiler.py
index ef9b870..c940fe9 100755
--- a/startop/scripts/iorap/compiler.py
+++ b/startop/scripts/iorap/compiler.py
@@ -29,6 +29,7 @@
 import sys
 import tempfile
 from pathlib import Path
+from datetime import timedelta
 from typing import Iterable, Optional, List
 
 DIR = os.path.abspath(os.path.dirname(__file__))
@@ -173,7 +174,7 @@
   return trace_file
 
 def calc_trace_end_time(trace2db: Trace2Db,
-                        trace_duration: Optional[int]) -> float:
+                        trace_duration: Optional[timedelta]) -> float:
   """
   Calculates the end time based on the trace duration.
   The start time is the first receiving mm file map event.
@@ -189,9 +190,9 @@
       MmFilemapAddToPageCache.raw_ftrace_entry).order_by(
       RawFtraceEntry.timestamp).first()
 
-  return first_event.raw_ftrace_entry.timestamp + trace_duration
+  return first_event.raw_ftrace_entry.timestamp + trace_duration.total_seconds()
 
-def query_add_to_page_cache(trace2db: Trace2Db, trace_duration: Optional[int]):
+def query_add_to_page_cache(trace2db: Trace2Db, trace_duration: Optional[timedelta]):
   end_time = calc_trace_end_time(trace2db, trace_duration)
   # SELECT * FROM tbl ORDER BY id;
   return trace2db.session.query(MmFilemapAddToPageCache).join(
@@ -210,7 +211,7 @@
 
 def run(sql_db_path:str,
         trace_file:str,
-        trace_duration:Optional[int],
+        trace_duration:Optional[timedelta],
         output_file:str,
         inode_table:str,
         filter:List[str]) -> int:
@@ -292,11 +293,14 @@
   if options.sql_db:
     sql_db_path = options.sql_db
 
+  trace_duration = timedelta(milliseconds=options.trace_duration) if \
+    options.trace_duration is not None else None
+
   # if the input is systrace
   if options.trace_file:
     return run(sql_db_path,
                options.trace_file,
-               options.trace_duration,
+               trace_duration,
                options.output_file,
                inode_table,
                options.filter)
@@ -308,7 +312,7 @@
                                          trace_file.name)
     return run(sql_db_path,
                trace_file.name,
-               options.trace_duration,
+               trace_duration,
                options.output_file,
                inode_table,
                options.filter)
diff --git a/startop/scripts/iorap/compiler_test.py b/startop/scripts/iorap/compiler_test.py
index b5d28b0..1a9f059 100644
--- a/startop/scripts/iorap/compiler_test.py
+++ b/startop/scripts/iorap/compiler_test.py
@@ -70,9 +70,9 @@
   # 10ms duration
   expected = os.path.join(DIR,
                           'test_fixtures/compiler/test_result_with_duration.TraceFile.pb')
-  assert_compile_result(output, expected, '--duration', '10')
+  assert_compile_result(output, expected, '--duration', '10000')
 
   # 30ms duration
   expected = os.path.join(DIR,
                           'test_fixtures/compiler/test_result_without_duration.TraceFile.pb')
-  assert_compile_result(output, expected, '--duration', '30')
+  assert_compile_result(output, expected, '--duration', '30000')
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 42a5501..0f1ae3d 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -4058,6 +4058,53 @@
         public static final String DEFAULT_SORT_ORDER = DELIVERY_TIME + " DESC";
 
         /**
+         * The timestamp in millisecond of when the device received the message.
+         * <P>Type: BIGINT</P>
+         */
+        public static final String RECEIVED_TIME = "received_time";
+
+        /**
+         * Indicates that whether the message has been broadcasted to the application.
+         * <P>Type: BOOLEAN</P>
+         */
+        public static final String MESSAGE_BROADCASTED = "message_broadcasted";
+
+        /**
+         * The Warning Area Coordinates Elements. This element is used for geo-fencing purpose.
+         *
+         * The geometry and its coordinates are separated vertical bar, the first item is the
+         * geometry type and the remaining items are the parameter of this geometry.
+         *
+         * Only circle and polygon are supported. The coordinates are represented in Horizontal
+         * coordinates format.
+         *
+         * Coordinate encoding:
+         * "latitude, longitude"
+         * where -90.00000 <= latitude <= 90.00000 and -180.00000 <= longitude <= 180.00000
+         *
+         * Polygon encoding:
+         * "polygon|lat1,lng1|lat2,lng2|...|latn,lngn"
+         * lat1,lng1 ... latn,lngn are the vertices coordinate of the polygon.
+         *
+         * Circle encoding:
+         * "circle|lat,lng|radius".
+         * lat,lng is the center of the circle. The unit of circle's radius is meter.
+         *
+         * Example:
+         * "circle|0,0|100" mean a circle which center located at (0,0) and its radius is 100 meter.
+         * "polygon|0,1.5|0,1|1,1|1,0" mean a polygon has vertices (0,1.5),(0,1),(1,1),(1,0).
+         *
+         * There could be more than one geometry store in this field, they are separated by a
+         * semicolon.
+         *
+         * Example:
+         * "circle|0,0|100;polygon|0,0|0,1.5|1,1|1,0;circle|100.123,100|200.123"
+         *
+         * <P>Type: TEXT</P>
+         */
+        public static final String GEOMETRIES = "geometries";
+
+        /**
          * Query columns for instantiating {@link android.telephony.CellBroadcastMessage} objects.
          */
         public static final String[] QUERY_COLUMNS = {
@@ -4082,6 +4129,33 @@
                 CMAS_URGENCY,
                 CMAS_CERTAINTY
         };
+
+        /**
+         * Query columns for instantiating {@link android.telephony.SmsCbMessage} objects.
+         */
+        public static final String[] QUERY_COLUMNS_FWK = {
+                _ID,
+                GEOGRAPHICAL_SCOPE,
+                PLMN,
+                LAC,
+                CID,
+                SERIAL_NUMBER,
+                SERVICE_CATEGORY,
+                LANGUAGE_CODE,
+                MESSAGE_BODY,
+                MESSAGE_FORMAT,
+                MESSAGE_PRIORITY,
+                ETWS_WARNING_TYPE,
+                CMAS_MESSAGE_CLASS,
+                CMAS_CATEGORY,
+                CMAS_RESPONSE_TYPE,
+                CMAS_SEVERITY,
+                CMAS_URGENCY,
+                CMAS_CERTAINTY,
+                RECEIVED_TIME,
+                MESSAGE_BROADCASTED,
+                GEOMETRIES
+        };
     }
 
     /**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7d9462a..654b54d 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1403,6 +1403,13 @@
             "read_only_apn_fields_string_array";
 
     /**
+     * Default value of APN types field if not specified by user when adding/modifying an APN.
+     * @hide
+     */
+    public static final String KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY =
+            "apn_settings_default_apn_types_string_array";
+
+    /**
      * Boolean indicating if intent for emergency call state changes should be broadcast
      * @hide
      */
@@ -3275,6 +3282,7 @@
         sDefaults.putBoolean(KEY_ALLOW_ADDING_APNS_BOOL, true);
         sDefaults.putStringArray(KEY_READ_ONLY_APN_TYPES_STRING_ARRAY, new String[] {"dun"});
         sDefaults.putStringArray(KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_SEVERE_WHEN_EXTREME_DISABLED_BOOL, true);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 36e8123..bb2269f 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -551,7 +551,6 @@
      *
      * @param context Context of the application to check.
      * @return whether the app is authorized to manage this subscription per its metadata.
-     * @throws UnsupportedOperationException if this subscription is not embedded.
      * @hide
      * @deprecated - Do not use.
      */
@@ -567,15 +566,11 @@
      * @param context Any context.
      * @param packageName Package name of the app to check.
      * @return whether the app is authorized to manage this subscription per its metadata.
-     * @throws UnsupportedOperationException if this subscription is not embedded.
      * @hide
      * @deprecated - Do not use.
      */
     @Deprecated
     public boolean canManageSubscription(Context context, String packageName) {
-        if (!isEmbedded()) {
-            throw new UnsupportedOperationException("Not an embedded subscription");
-        }
         List<UiccAccessRule> allAccessRules = getAllAccessRules();
         if (allAccessRules == null) {
             return false;
@@ -606,9 +601,6 @@
      */
     @SystemApi
     public @Nullable List<UiccAccessRule> getAccessRules() {
-        if (!isEmbedded()) {
-            throw new UnsupportedOperationException("Not an embedded subscription");
-        }
         if (mNativeAccessRules == null) return null;
         return Arrays.asList(mNativeAccessRules);
     }
@@ -619,9 +611,6 @@
      * @hide
      */
     public @Nullable List<UiccAccessRule> getAllAccessRules() {
-        if (!isEmbedded()) {
-            throw new UnsupportedOperationException("Not an embedded subscription");
-        }
         List<UiccAccessRule> merged = new ArrayList<>();
         if (mNativeAccessRules != null) merged.addAll(getAccessRules());
         if (mCarrierConfigAccessRules != null) {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index a84c916..5e47e49 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2588,7 +2588,6 @@
      *
      * @param info The subscription to check.
      * @return whether the app is authorized to manage this subscription per its metadata.
-     * @throws IllegalArgumentException if this subscription is not embedded.
      */
     public boolean canManageSubscription(SubscriptionInfo info) {
         return canManageSubscription(info, mContext.getPackageName());
@@ -2604,13 +2603,9 @@
      * @param info The subscription to check.
      * @param packageName Package name of the app to check.
      * @return whether the app is authorized to manage this subscription per its access rules.
-     * @throws IllegalArgumentException if this subscription is not embedded.
      * @hide
      */
     public boolean canManageSubscription(SubscriptionInfo info, String packageName) {
-        if (!info.isEmbedded()) {
-            throw new IllegalArgumentException("Not an embedded subscription");
-        }
         if (info.getAllAccessRules() == null) {
             return false;
         }
@@ -2857,8 +2852,7 @@
      *
      * @throws SecurityException if the caller doesn't meet the requirements
      *             outlined above.
-     * @throws IllegalArgumentException if the some subscriptions in the list doesn't exist,
-     *             or the groupUuid doesn't exist.
+     * @throws IllegalArgumentException if the some subscriptions in the list doesn't exist.
      * @throws IllegalStateException if Telephony service is in bad state.
      *
      * @param subIdList list of subId that need adding into the group
@@ -3012,7 +3006,7 @@
         // to the caller.
         boolean hasCarrierPrivilegePermission = TelephonyManager.from(mContext)
                 .hasCarrierPrivileges(info.getSubscriptionId())
-                || (info.isEmbedded() && canManageSubscription(info));
+                || canManageSubscription(info);
         return hasCarrierPrivilegePermission;
     }
 
diff --git a/telephony/java/com/android/internal/telephony/CbGeoUtils.java b/telephony/java/com/android/internal/telephony/CbGeoUtils.java
new file mode 100644
index 0000000..73dd822
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/CbGeoUtils.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.annotation.NonNull;
+import android.telephony.Rlog;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+/**
+ * This utils class is specifically used for geo-targeting of CellBroadcast messages.
+ * The coordinates used by this utils class are latitude and longitude, but some algorithms in this
+ * class only use them as coordinates on plane, so the calculation will be inaccurate. So don't use
+ * this class for anything other then geo-targeting of cellbroadcast messages.
+ */
+public class CbGeoUtils {
+    /** Geometric interface. */
+    public interface Geometry {
+        /**
+         * Determines if the given point {@code p} is inside the geometry.
+         * @param p point in latitude, longitude format.
+         * @return {@code True} if the given point is inside the geometry.
+         */
+        boolean contains(LatLng p);
+    }
+
+    /**
+     * Tolerance for determining if the value is 0. If the absolute value of a value is less than
+     * this tolerance, it will be treated as 0.
+     */
+    public static final double EPS = 1e-7;
+
+    /** The radius of earth. */
+    public static final int EARTH_RADIUS_METER = 6371 * 1000;
+
+    private static final String TAG = "CbGeoUtils";
+
+    /** The TLV tags of WAC, defined in ATIS-0700041 5.2.3 WAC tag coding. */
+    public static final int GEO_FENCING_MAXIMUM_WAIT_TIME = 0x01;
+    public static final int GEOMETRY_TYPE_POLYGON = 0x02;
+    public static final int GEOMETRY_TYPE_CIRCLE = 0x03;
+
+    /** The identifier of geometry in the encoded string. */
+    private static final String CIRCLE_SYMBOL = "circle";
+    private static final String POLYGON_SYMBOL = "polygon";
+
+    /** Point represent by (latitude, longitude). */
+    public static class LatLng {
+        public final double lat;
+        public final double lng;
+
+        /**
+         * Constructor.
+         * @param lat latitude, range [-90, 90]
+         * @param lng longitude, range [-180, 180]
+         */
+        public LatLng(double lat, double lng) {
+            this.lat = lat;
+            this.lng = lng;
+        }
+
+        /**
+         * @param p the point use to calculate the subtraction result.
+         * @return the result of this point subtract the given point {@code p}.
+         */
+        public LatLng subtract(LatLng p) {
+            return new LatLng(lat - p.lat, lng - p.lng);
+        }
+
+        /**
+         * Calculate the distance in meter between this point and the given point {@code p}.
+         * @param p the point use to calculate the distance.
+         * @return the distance in meter.
+         */
+        public double distance(LatLng p) {
+            double dlat = Math.sin(0.5 * Math.toRadians(lat - p.lat));
+            double dlng = Math.sin(0.5 * Math.toRadians(lng - p.lng));
+            double x = dlat * dlat
+                    + dlng * dlng * Math.cos(Math.toRadians(lat)) * Math.cos(Math.toRadians(p.lat));
+            return 2 * Math.atan2(Math.sqrt(x), Math.sqrt(1 - x)) * EARTH_RADIUS_METER;
+        }
+
+        @Override
+        public String toString() {
+            return "(" + lat + "," + lng + ")";
+        }
+    }
+
+    /**
+     * The class represents a simple polygon with at least 3 points.
+     */
+    public static class Polygon implements Geometry {
+        /**
+         * In order to reduce the loss of precision in floating point calculations, all vertices
+         * of the polygon are scaled. Set the value of scale to 1000 can take into account the
+         * actual distance accuracy of 1 meter if the EPS is 1e-7 during the calculation.
+         */
+        private static final double SCALE = 1000.0;
+
+        private final List<LatLng> mVertices;
+        private final List<Point> mScaledVertices;
+        private final LatLng mOrigin;
+
+        /**
+         * Constructs a simple polygon from the given vertices. The adjacent two vertices are
+         * connected to form an edge of the polygon. The polygon has at least 3 vertices, and the
+         * last vertices and the first vertices must be adjacent.
+         *
+         * The longitude difference in the vertices should be less than 180 degree.
+         */
+        public Polygon(@NonNull List<LatLng> vertices) {
+            mVertices = vertices;
+
+            // Find the point with smallest longitude as the mOrigin point.
+            int idx = 0;
+            for (int i = 1; i < vertices.size(); i++) {
+                if (vertices.get(i).lng < vertices.get(idx).lng) {
+                    idx = i;
+                }
+            }
+            mOrigin = vertices.get(idx);
+
+            mScaledVertices = vertices.stream()
+                    .map(latLng -> convertAndScaleLatLng(latLng))
+                    .collect(Collectors.toList());
+        }
+
+        public List<LatLng> getVertices() {
+            return mVertices;
+        }
+
+        /**
+         * Check if the given point {@code p} is inside the polygon. This method counts the number
+         * of times the polygon winds around the point P, A.K.A "winding number". The point is
+         * outside only when this "winding number" is 0.
+         *
+         * If a point is on the edge of the polygon, it is also considered to be inside the polygon.
+         */
+        @Override
+        public boolean contains(LatLng latLng) {
+            Point p = convertAndScaleLatLng(latLng);
+
+            int n = mScaledVertices.size();
+            int windingNumber = 0;
+            for (int i = 0; i < n; i++) {
+                Point a = mScaledVertices.get(i);
+                Point b = mScaledVertices.get((i + 1) % n);
+
+                // CCW is counterclockwise
+                // CCW = ab x ap
+                // CCW > 0 -> ap is on the left side of ab
+                // CCW == 0 -> ap is on the same line of ab
+                // CCW < 0 -> ap is on the right side of ab
+                int ccw = sign(crossProduct(b.subtract(a), p.subtract(a)));
+
+                if (ccw == 0) {
+                    if (Math.min(a.x, b.x) <= p.x && p.x <= Math.max(a.x, b.x)
+                            && Math.min(a.y, b.y) <= p.y && p.y <= Math.max(a.y, b.y)) {
+                        return true;
+                    }
+                } else {
+                    if (sign(a.y - p.y) <= 0) {
+                        // upward crossing
+                        if (ccw > 0 && sign(b.y - p.y) > 0) {
+                            ++windingNumber;
+                        }
+                    } else {
+                        // downward crossing
+                        if (ccw < 0 && sign(b.y - p.y) <= 0) {
+                            --windingNumber;
+                        }
+                    }
+                }
+            }
+            return windingNumber != 0;
+        }
+
+        /**
+         * Move the given point {@code latLng} to the coordinate system with {@code mOrigin} as the
+         * origin and scale it. {@code mOrigin} is selected from the vertices of a polygon, it has
+         * the smallest longitude value among all of the polygon vertices.
+         *
+         * @param latLng the point need to be converted and scaled.
+         * @Return a {@link Point} object.
+         */
+        private Point convertAndScaleLatLng(LatLng latLng) {
+            double x = latLng.lat - mOrigin.lat;
+            double y = latLng.lng - mOrigin.lng;
+
+            // If the point is in different hemispheres(western/eastern) than the mOrigin, and the
+            // edge between them cross the 180th meridian, then its relative coordinates will be
+            // extended.
+            // For example, suppose the longitude of the mOrigin is -178, and the longitude of the
+            // point to be converted is 175, then the longitude after the conversion is -8.
+            // calculation: (-178 - 8) - (-178).
+            if (sign(mOrigin.lng) != 0 && sign(mOrigin.lng) != sign(latLng.lng)) {
+                double distCross0thMeridian = Math.abs(mOrigin.lng) + Math.abs(latLng.lng);
+                if (sign(distCross0thMeridian * 2 - 360) > 0) {
+                    y = sign(mOrigin.lng) * (360 - distCross0thMeridian);
+                }
+            }
+            return new Point(x * SCALE, y * SCALE);
+        }
+
+        private static double crossProduct(Point a, Point b) {
+            return a.x * b.y - a.y * b.x;
+        }
+
+        static final class Point {
+            public final double x;
+            public final double y;
+
+            Point(double x, double y) {
+                this.x = x;
+                this.y = y;
+            }
+
+            public Point subtract(Point p) {
+                return new Point(x - p.x, y - p.y);
+            }
+        }
+    }
+
+    /** The class represents a circle. */
+    public static class Circle implements Geometry {
+        private final LatLng mCenter;
+        private final double mRadiusMeter;
+
+        public Circle(LatLng center, double radiusMeter) {
+            this.mCenter = center;
+            this.mRadiusMeter = radiusMeter;
+        }
+
+        public LatLng getCenter() {
+            return mCenter;
+        }
+
+        public double getRadius() {
+            return mRadiusMeter;
+        }
+
+        @Override
+        public boolean contains(LatLng p) {
+            return mCenter.distance(p) <= mRadiusMeter;
+        }
+    }
+
+    /**
+     * Parse the geometries from the encoded string {@code str}. The string must follow the
+     * geometry encoding specified by {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+     */
+    @NonNull
+    public static List<Geometry> parseGeometriesFromString(@NonNull String str) {
+        List<Geometry> geometries = new ArrayList<>();
+        for (String geometryStr : str.split("\\s*;\\s*")) {
+            String[] geoParameters = geometryStr.split("\\s*\\|\\s*");
+            switch (geoParameters[0]) {
+                case CIRCLE_SYMBOL:
+                    geometries.add(new Circle(parseLatLngFromString(geoParameters[1]),
+                            Double.parseDouble(geoParameters[2])));
+                    break;
+                case POLYGON_SYMBOL:
+                    List<LatLng> vertices = new ArrayList<>(geoParameters.length - 1);
+                    for (int i = 1; i < geoParameters.length; i++) {
+                        vertices.add(parseLatLngFromString(geoParameters[i]));
+                    }
+                    geometries.add(new Polygon(vertices));
+                    break;
+                default:
+                    Rlog.e(TAG, "Invalid geometry format " + geometryStr);
+            }
+        }
+        return geometries;
+    }
+
+    /**
+     * Encode a list of geometry objects to string. The encoding format is specified by
+     * {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+     *
+     * @param geometries the list of geometry objects need to be encoded.
+     * @return the encoded string.
+     */
+    @NonNull
+    public static String encodeGeometriesToString(@NonNull List<Geometry> geometries) {
+        return geometries.stream()
+                .map(geometry -> encodeGeometryToString(geometry))
+                .filter(encodedStr -> !TextUtils.isEmpty(encodedStr))
+                .collect(Collectors.joining(";"));
+    }
+
+
+    /**
+     * Encode the geometry object to string. The encoding format is specified by
+     * {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}.
+     * @param geometry the geometry object need to be encoded.
+     * @return the encoded string.
+     */
+    @NonNull
+    private static String encodeGeometryToString(@NonNull Geometry geometry) {
+        StringBuilder sb = new StringBuilder();
+        if (geometry instanceof Polygon) {
+            sb.append(POLYGON_SYMBOL);
+            for (LatLng latLng : ((Polygon) geometry).getVertices()) {
+                sb.append("|");
+                sb.append(latLng.lat);
+                sb.append(",");
+                sb.append(latLng.lng);
+            }
+        } else if (geometry instanceof Circle) {
+            sb.append(CIRCLE_SYMBOL);
+            Circle circle = (Circle) geometry;
+
+            // Center
+            sb.append("|");
+            sb.append(circle.getCenter().lat);
+            sb.append(",");
+            sb.append(circle.getCenter().lng);
+
+            // Radius
+            sb.append("|");
+            sb.append(circle.getRadius());
+        } else {
+            Rlog.e(TAG, "Unsupported geometry object " + geometry);
+            return null;
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Parse {@link LatLng} from {@link String}. Latitude and longitude are separated by ",".
+     * Example: "13.56,-55.447".
+     *
+     * @param str encoded lat/lng string.
+     * @Return {@link LatLng} object.
+     */
+    @NonNull
+    public static LatLng parseLatLngFromString(@NonNull String str) {
+        String[] latLng = str.split("\\s*,\\s*");
+        return new LatLng(Double.parseDouble(latLng[0]), Double.parseDouble(latLng[1]));
+    }
+
+    /**
+     * @Return the sign of the given value {@code value} with the specified tolerance. Return 1
+     * means the sign is positive, -1 means negative, 0 means the value will be treated as 0.
+     */
+    public static int sign(double value) {
+        if (value > EPS) return 1;
+        if (value < -EPS) return -1;
+        return 0;
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/SmsCbMessage.java b/telephony/java/com/android/internal/telephony/SmsCbMessage.java
index 046bf8c..b9edb9f 100644
--- a/telephony/java/com/android/internal/telephony/SmsCbMessage.java
+++ b/telephony/java/com/android/internal/telephony/SmsCbMessage.java
@@ -16,8 +16,17 @@
 
 package android.telephony;
 
+import android.annotation.Nullable;
+import android.content.ContentValues;
+import android.database.Cursor;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.provider.Telephony.CellBroadcasts;
+
+import com.android.internal.telephony.CbGeoUtils;
+import com.android.internal.telephony.CbGeoUtils.Geometry;
+
+import java.util.List;
 
 /**
  * Parcelable object containing a received cell broadcast message. There are four different types
@@ -138,12 +147,31 @@
     /** CMAS warning notification information (CMAS warnings only). */
     private final SmsCbCmasInfo mCmasWarningInfo;
 
+    /** UNIX timestamp of when the message was received. */
+    private final long mReceivedTimeMillis;
+
+    /** CMAS warning area coordinates. */
+    private final List<Geometry> mGeometries;
+
     /**
      * Create a new SmsCbMessage with the specified data.
      */
     public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
             SmsCbLocation location, int serviceCategory, String language, String body,
             int priority, SmsCbEtwsInfo etwsWarningInfo, SmsCbCmasInfo cmasWarningInfo) {
+
+        this(messageFormat, geographicalScope, serialNumber, location, serviceCategory, language,
+                body, priority, etwsWarningInfo, cmasWarningInfo, null /* geometries */,
+                System.currentTimeMillis());
+    }
+
+    /**
+     * Create a new {@link SmsCbMessage} with the warning area coordinates information.
+     */
+    public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
+            SmsCbLocation location, int serviceCategory, String language, String body,
+            int priority, SmsCbEtwsInfo etwsWarningInfo, SmsCbCmasInfo cmasWarningInfo,
+            List<Geometry> geometries, long receivedTimeMillis) {
         mMessageFormat = messageFormat;
         mGeographicalScope = geographicalScope;
         mSerialNumber = serialNumber;
@@ -154,6 +182,8 @@
         mPriority = priority;
         mEtwsWarningInfo = etwsWarningInfo;
         mCmasWarningInfo = cmasWarningInfo;
+        mReceivedTimeMillis = receivedTimeMillis;
+        mGeometries = geometries;
     }
 
     /** Create a new SmsCbMessage object from a Parcel. */
@@ -184,6 +214,9 @@
                 mEtwsWarningInfo = null;
                 mCmasWarningInfo = null;
         }
+        mReceivedTimeMillis = in.readLong();
+        String geoStr = in.readString();
+        mGeometries = geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null;
     }
 
     /**
@@ -214,6 +247,9 @@
             // no ETWS or CMAS warning information
             dest.writeInt('0');
         }
+        dest.writeLong(mReceivedTimeMillis);
+        dest.writeString(
+                mGeometries != null ? CbGeoUtils.encodeGeometriesToString(mGeometries) : null);
     }
 
     public static final Parcelable.Creator<SmsCbMessage> CREATOR
@@ -293,6 +329,24 @@
     }
 
     /**
+     * Get the warning area coordinates information represent by polygons and circles.
+     * @return a list of geometries, {@link Nullable} means there is no coordinate information
+     * associated to this message.
+     */
+    @Nullable
+    public List<Geometry> getGeometries() {
+        return mGeometries;
+    }
+
+    /**
+     * Get the time when this message was received.
+     * @return the time in millisecond
+     */
+    public long getReceivedTime() {
+        return mReceivedTimeMillis;
+    }
+
+    /**
      * Get the message format ({@link #MESSAGE_FORMAT_3GPP} or {@link #MESSAGE_FORMAT_3GPP2}).
      * @return an integer representing 3GPP or 3GPP2 message format
      */
@@ -368,7 +422,10 @@
                 + mServiceCategory + ", language=" + mLanguage + ", body=" + mBody
                 + ", priority=" + mPriority
                 + (mEtwsWarningInfo != null ? (", " + mEtwsWarningInfo.toString()) : "")
-                + (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "") + '}';
+                + (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "")
+                + ", geo=" + (mGeometries != null
+                ? CbGeoUtils.encodeGeometriesToString(mGeometries) : "null")
+                + '}';
     }
 
     /**
@@ -379,4 +436,171 @@
     public int describeContents() {
         return 0;
     }
+
+    /**
+     * @return the {@link ContentValues} instance that includes the cell broadcast data.
+     */
+    public ContentValues getContentValues() {
+        ContentValues cv = new ContentValues(16);
+        cv.put(CellBroadcasts.GEOGRAPHICAL_SCOPE, mGeographicalScope);
+        if (mLocation.getPlmn() != null) {
+            cv.put(CellBroadcasts.PLMN, mLocation.getPlmn());
+        }
+        if (mLocation.getLac() != -1) {
+            cv.put(CellBroadcasts.LAC, mLocation.getLac());
+        }
+        if (mLocation.getCid() != -1) {
+            cv.put(CellBroadcasts.CID, mLocation.getCid());
+        }
+        cv.put(CellBroadcasts.SERIAL_NUMBER, getSerialNumber());
+        cv.put(CellBroadcasts.SERVICE_CATEGORY, getServiceCategory());
+        cv.put(CellBroadcasts.LANGUAGE_CODE, getLanguageCode());
+        cv.put(CellBroadcasts.MESSAGE_BODY, getMessageBody());
+        cv.put(CellBroadcasts.MESSAGE_FORMAT, getMessageFormat());
+        cv.put(CellBroadcasts.MESSAGE_PRIORITY, getMessagePriority());
+
+        SmsCbEtwsInfo etwsInfo = getEtwsWarningInfo();
+        if (etwsInfo != null) {
+            cv.put(CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType());
+        }
+
+        SmsCbCmasInfo cmasInfo = getCmasWarningInfo();
+        if (cmasInfo != null) {
+            cv.put(CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass());
+            cv.put(CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory());
+            cv.put(CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType());
+            cv.put(CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity());
+            cv.put(CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency());
+            cv.put(CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty());
+        }
+
+        cv.put(CellBroadcasts.RECEIVED_TIME, mReceivedTimeMillis);
+
+        if (mGeometries != null) {
+            cv.put(CellBroadcasts.GEOMETRIES, CbGeoUtils.encodeGeometriesToString(mGeometries));
+        } else {
+            cv.put(CellBroadcasts.GEOMETRIES, (String) null);
+        }
+
+        return cv;
+    }
+
+    /**
+     * Create a {@link SmsCbMessage} instance from a row in the cell broadcast database.
+     * @param cursor an open SQLite cursor pointing to the row to read
+     * @return a {@link SmsCbMessage} instance.
+     * @throws IllegalArgumentException if one of the required columns is missing
+     */
+    public static SmsCbMessage createFromCursor(Cursor cursor) {
+        int geoScope = cursor.getInt(
+                cursor.getColumnIndexOrThrow(CellBroadcasts.GEOGRAPHICAL_SCOPE));
+        int serialNum = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERIAL_NUMBER));
+        int category = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SERVICE_CATEGORY));
+        String language = cursor.getString(
+                cursor.getColumnIndexOrThrow(CellBroadcasts.LANGUAGE_CODE));
+        String body = cursor.getString(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_BODY));
+        int format = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_FORMAT));
+        int priority = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_PRIORITY));
+
+        String plmn;
+        int plmnColumn = cursor.getColumnIndex(CellBroadcasts.PLMN);
+        if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) {
+            plmn = cursor.getString(plmnColumn);
+        } else {
+            plmn = null;
+        }
+
+        int lac;
+        int lacColumn = cursor.getColumnIndex(CellBroadcasts.LAC);
+        if (lacColumn != -1 && !cursor.isNull(lacColumn)) {
+            lac = cursor.getInt(lacColumn);
+        } else {
+            lac = -1;
+        }
+
+        int cid;
+        int cidColumn = cursor.getColumnIndex(CellBroadcasts.CID);
+        if (cidColumn != -1 && !cursor.isNull(cidColumn)) {
+            cid = cursor.getInt(cidColumn);
+        } else {
+            cid = -1;
+        }
+
+        SmsCbLocation location = new SmsCbLocation(plmn, lac, cid);
+
+        SmsCbEtwsInfo etwsInfo;
+        int etwsWarningTypeColumn = cursor.getColumnIndex(CellBroadcasts.ETWS_WARNING_TYPE);
+        if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) {
+            int warningType = cursor.getInt(etwsWarningTypeColumn);
+            etwsInfo = new SmsCbEtwsInfo(warningType, false, false, false, null);
+        } else {
+            etwsInfo = null;
+        }
+
+        SmsCbCmasInfo cmasInfo = null;
+        int cmasMessageClassColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_MESSAGE_CLASS);
+        if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) {
+            int messageClass = cursor.getInt(cmasMessageClassColumn);
+
+            int cmasCategory;
+            int cmasCategoryColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_CATEGORY);
+            if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) {
+                cmasCategory = cursor.getInt(cmasCategoryColumn);
+            } else {
+                cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
+            }
+
+            int responseType;
+            int cmasResponseTypeColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_RESPONSE_TYPE);
+            if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) {
+                responseType = cursor.getInt(cmasResponseTypeColumn);
+            } else {
+                responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
+            }
+
+            int severity;
+            int cmasSeverityColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_SEVERITY);
+            if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) {
+                severity = cursor.getInt(cmasSeverityColumn);
+            } else {
+                severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
+            }
+
+            int urgency;
+            int cmasUrgencyColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_URGENCY);
+            if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) {
+                urgency = cursor.getInt(cmasUrgencyColumn);
+            } else {
+                urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
+            }
+
+            int certainty;
+            int cmasCertaintyColumn = cursor.getColumnIndex(CellBroadcasts.CMAS_CERTAINTY);
+            if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) {
+                certainty = cursor.getInt(cmasCertaintyColumn);
+            } else {
+                certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
+            }
+
+            cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity,
+                    urgency, certainty);
+        }
+
+        String geoStr = cursor.getString(cursor.getColumnIndex(CellBroadcasts.GEOMETRIES));
+        List<Geometry> geometries =
+                geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null;
+
+        long receivedTimeSec = cursor.getLong(
+                cursor.getColumnIndexOrThrow(CellBroadcasts.RECEIVED_TIME));
+
+        return new SmsCbMessage(format, geoScope, serialNum, location, category,
+                language, body, priority, etwsInfo, cmasInfo, geometries, receivedTimeSec);
+    }
+
+    /**
+     * @return {@code True} if this message needs geo-fencing check.
+     */
+    public boolean needGeoFencingCheck() {
+        return mGeometries != null;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
index 8015b07..dca4e6b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
@@ -22,58 +22,36 @@
 import static android.telephony.SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE;
 import static android.telephony.SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
 import android.telephony.SmsCbLocation;
 import android.telephony.SmsCbMessage;
 import android.util.Pair;
+import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.internal.telephony.CbGeoUtils;
+import com.android.internal.telephony.CbGeoUtils.Circle;
+import com.android.internal.telephony.CbGeoUtils.Geometry;
+import com.android.internal.telephony.CbGeoUtils.LatLng;
+import com.android.internal.telephony.CbGeoUtils.Polygon;
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.gsm.GsmSmsCbMessage.GeoFencingTriggerMessage.CellBroadcastIdentity;
+import com.android.internal.telephony.gsm.SmsCbHeader.DataCodingScheme;
 
 import java.io.UnsupportedEncodingException;
-import java.util.Locale;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * Parses a GSM or UMTS format SMS-CB message into an {@link SmsCbMessage} object. The class is
  * public because {@link #createSmsCbMessage(SmsCbLocation, byte[][])} is used by some test cases.
  */
 public class GsmSmsCbMessage {
-
-    /**
-     * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
-     */
-    private static final String[] LANGUAGE_CODES_GROUP_0 = {
-            Locale.GERMAN.getLanguage(),        // German
-            Locale.ENGLISH.getLanguage(),       // English
-            Locale.ITALIAN.getLanguage(),       // Italian
-            Locale.FRENCH.getLanguage(),        // French
-            new Locale("es").getLanguage(),     // Spanish
-            new Locale("nl").getLanguage(),     // Dutch
-            new Locale("sv").getLanguage(),     // Swedish
-            new Locale("da").getLanguage(),     // Danish
-            new Locale("pt").getLanguage(),     // Portuguese
-            new Locale("fi").getLanguage(),     // Finnish
-            new Locale("nb").getLanguage(),     // Norwegian
-            new Locale("el").getLanguage(),     // Greek
-            new Locale("tr").getLanguage(),     // Turkish
-            new Locale("hu").getLanguage(),     // Hungarian
-            new Locale("pl").getLanguage(),     // Polish
-            null
-    };
-
-    /**
-     * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
-     */
-    private static final String[] LANGUAGE_CODES_GROUP_2 = {
-            new Locale("cs").getLanguage(),     // Czech
-            new Locale("he").getLanguage(),     // Hebrew
-            new Locale("ar").getLanguage(),     // Arabic
-            new Locale("ru").getLanguage(),     // Russian
-            new Locale("is").getLanguage(),     // Icelandic
-            null, null, null, null, null, null, null, null, null, null, null
-    };
+    private static final String TAG = GsmSmsCbMessage.class.getSimpleName();
 
     private static final char CARRIAGE_RETURN = 0x0d;
 
@@ -114,8 +92,9 @@
      * @param pdus PDU bytes
      */
     public static SmsCbMessage createSmsCbMessage(Context context, SmsCbHeader header,
-                                                  SmsCbLocation location, byte[][] pdus)
+            SmsCbLocation location, byte[][] pdus)
             throws IllegalArgumentException {
+        long receivedTimeMillis = System.currentTimeMillis();
         if (header.isEtwsPrimaryNotification()) {
             // ETSI TS 23.041 ETWS Primary Notification message
             // ETWS primary message only contains 4 fields including serial number,
@@ -125,12 +104,41 @@
                     header.getSerialNumber(), location, header.getServiceCategory(), null,
                     getEtwsPrimaryMessage(context, header.getEtwsInfo().getWarningType()),
                     SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, header.getEtwsInfo(),
-                    header.getCmasInfo());
+                    header.getCmasInfo(), null /* geometries */, receivedTimeMillis);
+        } else if (header.isUmtsFormat()) {
+            // UMTS format has only 1 PDU
+            byte[] pdu = pdus[0];
+            Pair<String, String> cbData = parseUmtsBody(header, pdu);
+            String language = cbData.first;
+            String body = cbData.second;
+            int priority = header.isEmergencyMessage() ? SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY
+                    : SmsCbMessage.MESSAGE_PRIORITY_NORMAL;
+            int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH];
+            int wacDataOffset = SmsCbHeader.PDU_HEADER_LENGTH
+                    + 1 // number of pages
+                    + (PDU_BODY_PAGE_LENGTH + 1) * nrPages; // cb data
+
+            // Has Warning Area Coordinates information
+            List<Geometry> geometries = null;
+            if (pdu.length > wacDataOffset) {
+                try {
+                    geometries = parseWarningAreaCoordinates(pdu, wacDataOffset);
+                } catch (Exception ex) {
+                    // Catch the exception here, the message will be considered as having no WAC
+                    // information which means the message will be broadcasted directly.
+                    Slog.e(TAG, "Can't parse warning area coordinates, ex = " + ex.toString());
+                }
+            }
+
+            return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
+                    header.getGeographicalScope(), header.getSerialNumber(), location,
+                    header.getServiceCategory(), language, body, priority,
+                    header.getEtwsInfo(), header.getCmasInfo(), geometries, receivedTimeMillis);
         } else {
             String language = null;
             StringBuilder sb = new StringBuilder();
             for (byte[] pdu : pdus) {
-                Pair<String, String> p = parseBody(header, pdu);
+                Pair<String, String> p = parseGsmBody(header, pdu);
                 language = p.first;
                 sb.append(p.second);
             }
@@ -140,154 +148,197 @@
             return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
                     header.getGeographicalScope(), header.getSerialNumber(), location,
                     header.getServiceCategory(), language, sb.toString(), priority,
-                    header.getEtwsInfo(), header.getCmasInfo());
+                    header.getEtwsInfo(), header.getCmasInfo(), null /* geometries */,
+                    receivedTimeMillis);
         }
     }
 
     /**
-     * Parse and unpack the body text according to the encoding in the DCS.
-     * After completing successfully this method will have assigned the body
-     * text into mBody, and optionally the language code into mLanguage
+     * Parse WEA Handset Action Message(WHAM) a.k.a geo-fencing trigger message.
+     *
+     * WEA Handset Action Message(WHAM) is a cell broadcast service message broadcast by the network
+     * to direct devices to perform a geo-fencing check on selected alerts.
+     *
+     * WEA Handset Action Message(WHAM) requirements from ATIS-0700041 section 4
+     * 1. The Warning Message contents of a WHAM shall be in Cell Broadcast(CB) data format as
+     * defined in TS 23.041.
+     * 2. The Warning Message Contents of WHAM shall be limited to one CB page(max 20 referenced
+     * WEA messages).
+     * 3. The broadcast area for a WHAM shall be the union of the broadcast areas of the referenced
+     * WEA message.
+     * @param pdu cell broadcast pdu, including the header
+     * @return {@link GeoFencingTriggerMessage} instance
+     */
+    public static GeoFencingTriggerMessage createGeoFencingTriggerMessage(byte[] pdu) {
+        try {
+            // Header length + 1(number of page). ATIS-0700041 define the number of page of
+            // geo-fencing trigger message is 1.
+            int whamOffset = SmsCbHeader.PDU_HEADER_LENGTH + 1;
+
+            BitStreamReader bitReader = new BitStreamReader(pdu, whamOffset);
+            int type = bitReader.read(4);
+            int length = bitReader.read(7);
+            // Skip the remained 5 bits
+            bitReader.skip();
+
+            int messageIdentifierCount = (length - 2) * 8 / 32;
+            List<CellBroadcastIdentity> cbIdentifiers = new ArrayList<>();
+            for (int i = 0; i < messageIdentifierCount; i++) {
+                // Both messageIdentifier and serialNumber are 16 bits integers.
+                // ATIS-0700041 Section 5.1.6
+                int messageIdentifier = bitReader.read(16);
+                int serialNumber = bitReader.read(16);
+                cbIdentifiers.add(new CellBroadcastIdentity(messageIdentifier, serialNumber));
+            }
+            return new GeoFencingTriggerMessage(type, cbIdentifiers);
+        } catch (Exception ex) {
+            Slog.e(TAG, "create geo-fencing trigger failed, ex = " + ex.toString());
+            return null;
+        }
+    }
+
+    private static List<Geometry> parseWarningAreaCoordinates(byte[] pdu, int wacOffset) {
+        // little-endian
+        int wacDataLength = (pdu[wacOffset + 1] << 8) | pdu[wacOffset];
+        int offset = wacOffset + 2;
+
+        if (offset + wacDataLength > pdu.length) {
+            throw new IllegalArgumentException("Invalid wac data, expected the length of pdu at"
+                    + "least " + offset + wacDataLength + ", actual is " + pdu.length);
+        }
+
+        BitStreamReader bitReader = new BitStreamReader(pdu, offset);
+
+        List<Geometry> geo = new ArrayList<>();
+        int remainedBytes = wacDataLength;
+        while (remainedBytes > 0) {
+            int type = bitReader.read(4);
+            int length = bitReader.read(10);
+            remainedBytes -= length;
+            // Skip the 2 remained bits
+            bitReader.skip();
+
+            switch (type) {
+                case CbGeoUtils.GEO_FENCING_MAXIMUM_WAIT_TIME:
+                    // TODO: handle the maximum wait time in cell broadcast provider.
+                    int maximumWaitTimeSec = bitReader.read(8);
+                    break;
+                case CbGeoUtils.GEOMETRY_TYPE_POLYGON:
+                    List<LatLng> latLngs = new ArrayList<>();
+                    // Each coordinate is represented by 44 bits integer.
+                    // ATIS-0700041 5.2.4 Coordinate coding
+                    int n = (length - 2) * 8 / 44;
+                    for (int i = 0; i < n; i++) {
+                        latLngs.add(getLatLng(bitReader));
+                    }
+                    // Skip the padding bits
+                    bitReader.skip();
+                    geo.add(new Polygon(latLngs));
+                    break;
+                case CbGeoUtils.GEOMETRY_TYPE_CIRCLE:
+                    LatLng center = getLatLng(bitReader);
+                    // radius = (wacRadius / 2^6). The unit of wacRadius is km, we use meter as the
+                    // distance unit during geo-fencing.
+                    // ATIS-0700041 5.2.5 radius coding
+                    double radius = (bitReader.read(20) * 1.0 / (1 << 6)) * 1000.0;
+                    geo.add(new Circle(center, radius));
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported geoType = " + type);
+            }
+        }
+        return geo;
+    }
+
+    /**
+     * The coordinate is (latitude, longitude), represented by a 44 bits integer.
+     * The coding is defined in ATIS-0700041 5.2.4
+     * @param bitReader
+     * @return coordinate (latitude, longitude)
+     */
+    private static LatLng getLatLng(BitStreamReader bitReader) {
+        // wacLatitude = floor(((latitude + 90) / 180) * 2^22)
+        // wacLongitude = floor(((longitude + 180) / 360) * 2^22)
+        int wacLat = bitReader.read(22);
+        int wacLng = bitReader.read(22);
+
+        // latitude = wacLatitude * 180 / 2^22 - 90
+        // longitude = wacLongitude * 360 / 2^22 -180
+        return new LatLng((wacLat * 180.0 / (1 << 22)) - 90, (wacLng * 360.0 / (1 << 22) - 180));
+    }
+
+    /**
+     * Parse and unpack the UMTS body text according to the encoding in the data coding scheme.
      *
      * @param header the message header to use
      * @param pdu the PDU to decode
-     * @return a Pair of Strings containing the language and body of the message
+     * @return a pair of string containing the language and body of the message in order
      */
-    private static Pair<String, String> parseBody(SmsCbHeader header, byte[] pdu) {
-        int encoding;
-        String language = null;
-        boolean hasLanguageIndicator = false;
-        int dataCodingScheme = header.getDataCodingScheme();
+    private static Pair<String, String> parseUmtsBody(SmsCbHeader header, byte[] pdu) {
+        // Payload may contain multiple pages
+        int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH];
+        String language = header.getDataCodingSchemeStructedData().language;
 
-        // Extract encoding and language from DCS, as defined in 3gpp TS 23.038,
-        // section 5.
-        switch ((dataCodingScheme & 0xf0) >> 4) {
-            case 0x00:
-                encoding = SmsConstants.ENCODING_7BIT;
-                language = LANGUAGE_CODES_GROUP_0[dataCodingScheme & 0x0f];
-                break;
-
-            case 0x01:
-                hasLanguageIndicator = true;
-                if ((dataCodingScheme & 0x0f) == 0x01) {
-                    encoding = SmsConstants.ENCODING_16BIT;
-                } else {
-                    encoding = SmsConstants.ENCODING_7BIT;
-                }
-                break;
-
-            case 0x02:
-                encoding = SmsConstants.ENCODING_7BIT;
-                language = LANGUAGE_CODES_GROUP_2[dataCodingScheme & 0x0f];
-                break;
-
-            case 0x03:
-                encoding = SmsConstants.ENCODING_7BIT;
-                break;
-
-            case 0x04:
-            case 0x05:
-                switch ((dataCodingScheme & 0x0c) >> 2) {
-                    case 0x01:
-                        encoding = SmsConstants.ENCODING_8BIT;
-                        break;
-
-                    case 0x02:
-                        encoding = SmsConstants.ENCODING_16BIT;
-                        break;
-
-                    case 0x00:
-                    default:
-                        encoding = SmsConstants.ENCODING_7BIT;
-                        break;
-                }
-                break;
-
-            case 0x06:
-            case 0x07:
-                // Compression not supported
-            case 0x09:
-                // UDH structure not supported
-            case 0x0e:
-                // Defined by the WAP forum not supported
-                throw new IllegalArgumentException("Unsupported GSM dataCodingScheme "
-                        + dataCodingScheme);
-
-            case 0x0f:
-                if (((dataCodingScheme & 0x04) >> 2) == 0x01) {
-                    encoding = SmsConstants.ENCODING_8BIT;
-                } else {
-                    encoding = SmsConstants.ENCODING_7BIT;
-                }
-                break;
-
-            default:
-                // Reserved values are to be treated as 7-bit
-                encoding = SmsConstants.ENCODING_7BIT;
-                break;
+        if (pdu.length < SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1)
+                * nrPages) {
+            throw new IllegalArgumentException("Pdu length " + pdu.length + " does not match "
+                    + nrPages + " pages");
         }
 
-        if (header.isUmtsFormat()) {
-            // Payload may contain multiple pages
-            int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH];
+        StringBuilder sb = new StringBuilder();
 
-            if (pdu.length < SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1)
-                    * nrPages) {
-                throw new IllegalArgumentException("Pdu length " + pdu.length + " does not match "
-                        + nrPages + " pages");
+        for (int i = 0; i < nrPages; i++) {
+            // Each page is 82 bytes followed by a length octet indicating
+            // the number of useful octets within those 82
+            int offset = SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) * i;
+            int length = pdu[offset + PDU_BODY_PAGE_LENGTH];
+
+            if (length > PDU_BODY_PAGE_LENGTH) {
+                throw new IllegalArgumentException("Page length " + length
+                        + " exceeds maximum value " + PDU_BODY_PAGE_LENGTH);
             }
 
-            StringBuilder sb = new StringBuilder();
-
-            for (int i = 0; i < nrPages; i++) {
-                // Each page is 82 bytes followed by a length octet indicating
-                // the number of useful octets within those 82
-                int offset = SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) * i;
-                int length = pdu[offset + PDU_BODY_PAGE_LENGTH];
-
-                if (length > PDU_BODY_PAGE_LENGTH) {
-                    throw new IllegalArgumentException("Page length " + length
-                            + " exceeds maximum value " + PDU_BODY_PAGE_LENGTH);
-                }
-
-                Pair<String, String> p = unpackBody(pdu, encoding, offset, length,
-                        hasLanguageIndicator, language);
-                language = p.first;
-                sb.append(p.second);
-            }
-            return new Pair<String, String>(language, sb.toString());
-        } else {
-            // Payload is one single page
-            int offset = SmsCbHeader.PDU_HEADER_LENGTH;
-            int length = pdu.length - offset;
-
-            return unpackBody(pdu, encoding, offset, length, hasLanguageIndicator, language);
+            Pair<String, String> p = unpackBody(pdu, offset, length,
+                    header.getDataCodingSchemeStructedData());
+            language = p.first;
+            sb.append(p.second);
         }
+        return new Pair(language, sb.toString());
+
     }
 
     /**
-     * Unpack body text from the pdu using the given encoding, position and
-     * length within the pdu
+     * Parse and unpack the GSM body text according to the encoding in the data coding scheme.
+     * @param header the message header to use
+     * @param pdu the PDU to decode
+     * @return a pair of string containing the language and body of the message in order
+     */
+    private static Pair<String, String> parseGsmBody(SmsCbHeader header, byte[] pdu) {
+        // Payload is one single page
+        int offset = SmsCbHeader.PDU_HEADER_LENGTH;
+        int length = pdu.length - offset;
+        return unpackBody(pdu, offset, length, header.getDataCodingSchemeStructedData());
+    }
+
+    /**
+     * Unpack body text from the pdu using the given encoding, position and length within the pdu.
      *
      * @param pdu The pdu
-     * @param encoding The encoding, as derived from the DCS
      * @param offset Position of the first byte to unpack
      * @param length Number of bytes to unpack
-     * @param hasLanguageIndicator true if the body text is preceded by a
-     *            language indicator. If so, this method will as a side-effect
-     *            assign the extracted language code into mLanguage
-     * @param language the language to return if hasLanguageIndicator is false
+     * @param dcs data coding scheme
      * @return a Pair of Strings containing the language and body of the message
      */
-    private static Pair<String, String> unpackBody(byte[] pdu, int encoding, int offset, int length,
-            boolean hasLanguageIndicator, String language) {
+    private static Pair<String, String> unpackBody(byte[] pdu, int offset, int length,
+            DataCodingScheme dcs) {
         String body = null;
 
-        switch (encoding) {
+        String language = dcs.language;
+        switch (dcs.encoding) {
             case SmsConstants.ENCODING_7BIT:
                 body = GsmAlphabet.gsm7BitPackedToString(pdu, offset, length * 8 / 7);
 
-                if (hasLanguageIndicator && body != null && body.length() > 2) {
+                if (dcs.hasLanguageIndicator && body != null && body.length() > 2) {
                     // Language is two GSM characters followed by a CR.
                     // The actual body text is offset by 3 characters.
                     language = body.substring(0, 2);
@@ -296,7 +347,7 @@
                 break;
 
             case SmsConstants.ENCODING_16BIT:
-                if (hasLanguageIndicator && pdu.length >= offset + 2) {
+                if (dcs.hasLanguageIndicator && pdu.length >= offset + 2) {
                     // Language is two GSM characters.
                     // The actual body text is offset by 2 bytes.
                     language = GsmAlphabet.gsm7BitPackedToString(pdu, offset, 2);
@@ -330,4 +381,105 @@
 
         return new Pair<String, String>(language, body);
     }
+
+    /** A class use to facilitate the processing of bits stream data. */
+    private static final class BitStreamReader {
+        /** The bits stream represent by a bytes array. */
+        private final byte[] mData;
+
+        /** The offset of the current byte. */
+        private int mCurrentOffset;
+
+        /**
+         * The remained bits of the current byte which have not been read. The most significant
+         * will be read first, so the remained bits are always the least significant bits.
+         */
+        private int mRemainedBit;
+
+        /**
+         * Constructor
+         * @param data bit stream data represent by byte array.
+         * @param offset the offset of the first byte.
+         */
+        BitStreamReader(byte[] data, int offset) {
+            mData = data;
+            mCurrentOffset = offset;
+            mRemainedBit = 8;
+        }
+
+        /**
+         * Read the first {@code count} bits.
+         * @param count the number of bits need to read
+         * @return {@code bits} represent by an 32-bits integer, therefore {@code count} must be no
+         * greater than 32.
+         */
+        public int read(int count) throws IndexOutOfBoundsException {
+            int val = 0;
+            while (count > 0) {
+                if (count >= mRemainedBit) {
+                    val <<= mRemainedBit;
+                    val |= mData[mCurrentOffset] & ((1 << mRemainedBit) - 1);
+                    count -= mRemainedBit;
+                    mRemainedBit = 8;
+                    ++mCurrentOffset;
+                } else {
+                    val <<= count;
+                    val |= (mData[mCurrentOffset] & ((1 << mRemainedBit) - 1))
+                            >> (mRemainedBit - count);
+                    mRemainedBit -= count;
+                    count = 0;
+                }
+            }
+            return val;
+        }
+
+        /**
+         * Skip the current bytes if the remained bits is less than 8. This is useful when
+         * processing the padding or reserved bits.
+         */
+        public void skip() {
+            if (mRemainedBit < 8) {
+                mRemainedBit = 8;
+                ++mCurrentOffset;
+            }
+        }
+    }
+
+    static final class GeoFencingTriggerMessage {
+        /**
+         * Indicate the list of active alerts share their warning area coordinates which means the
+         * broadcast area is the union of the broadcast areas of the active alerts in this list.
+         */
+        public static final int TYPE_ACTIVE_ALERT_SHARE_WAC = 2;
+
+        public final int type;
+        public final List<CellBroadcastIdentity> cbIdentifiers;
+
+        GeoFencingTriggerMessage(int type, @NonNull List<CellBroadcastIdentity> cbIdentifiers) {
+            this.type = type;
+            this.cbIdentifiers = cbIdentifiers;
+        }
+
+        boolean shouldShareBroadcastArea() {
+            return type == TYPE_ACTIVE_ALERT_SHARE_WAC;
+        }
+
+        static final class CellBroadcastIdentity {
+            public final int messageIdentifier;
+            public final int serialNumber;
+            CellBroadcastIdentity(int messageIdentifier, int serialNumber) {
+                this.messageIdentifier = messageIdentifier;
+                this.serialNumber = serialNumber;
+            }
+        }
+
+        @Override
+        public String toString() {
+            String identifiers = cbIdentifiers.stream()
+                    .map(cbIdentifier ->String.format("(msgId = %d, serial = %d)",
+                            cbIdentifier.messageIdentifier, cbIdentifier.serialNumber))
+                    .collect(Collectors.joining(","));
+            return "triggerType=" + type + " identifiers=" + identifiers;
+        }
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java
index 541ca8d..5ad2b9d 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java
@@ -215,9 +215,11 @@
     public static final int MESSAGE_ID_CMAS_ALERT_STATE_LOCAL_TEST_LANGUAGE
             = 0x112F; // 4399
 
-    /** End of CMAS Message Identifier range (including future extensions). */
-    public static final int MESSAGE_ID_CMAS_LAST_IDENTIFIER
-            = 0x112F; // 4399
+    /** CMAS Message Identifier for CMAS geo fencing trigger message. */
+    public static final int MESSAGE_ID_CMAS_GEO_FENCING_TRIGGER = 0x1130; // 4440
+
+    /** End of CMAS Message Identifier range. */
+    public static final int MESSAGE_ID_CMAS_LAST_IDENTIFIER = MESSAGE_ID_CMAS_GEO_FENCING_TRIGGER;
 
     /** End of PWS Message Identifier range (includes ETWS, CMAS, and future extensions). */
     public static final int MESSAGE_ID_PWS_LAST_IDENTIFIER
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index 996edfc..6bbff4b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -19,9 +19,12 @@
 import android.telephony.SmsCbCmasInfo;
 import android.telephony.SmsCbEtwsInfo;
 
+import com.android.internal.telephony.SmsConstants;
+
 import dalvik.annotation.compat.UnsupportedAppUsage;
 
 import java.util.Arrays;
+import java.util.Locale;
 
 /**
  * Parses a 3GPP TS 23.041 cell broadcast message header. This class is public for use by
@@ -34,6 +37,39 @@
  * The raw PDU is no longer sent to SMS CB applications.
  */
 public class SmsCbHeader {
+    /**
+     * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
+     */
+    private static final String[] LANGUAGE_CODES_GROUP_0 = {
+            Locale.GERMAN.getLanguage(),        // German
+            Locale.ENGLISH.getLanguage(),       // English
+            Locale.ITALIAN.getLanguage(),       // Italian
+            Locale.FRENCH.getLanguage(),        // French
+            new Locale("es").getLanguage(),     // Spanish
+            new Locale("nl").getLanguage(),     // Dutch
+            new Locale("sv").getLanguage(),     // Swedish
+            new Locale("da").getLanguage(),     // Danish
+            new Locale("pt").getLanguage(),     // Portuguese
+            new Locale("fi").getLanguage(),     // Finnish
+            new Locale("nb").getLanguage(),     // Norwegian
+            new Locale("el").getLanguage(),     // Greek
+            new Locale("tr").getLanguage(),     // Turkish
+            new Locale("hu").getLanguage(),     // Hungarian
+            new Locale("pl").getLanguage(),     // Polish
+            null
+    };
+
+    /**
+     * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
+     */
+    private static final String[] LANGUAGE_CODES_GROUP_2 = {
+            new Locale("cs").getLanguage(),     // Czech
+            new Locale("he").getLanguage(),     // Hebrew
+            new Locale("ar").getLanguage(),     // Arabic
+            new Locale("ru").getLanguage(),     // Russian
+            new Locale("is").getLanguage(),     // Icelandic
+            null, null, null, null, null, null, null, null, null, null, null
+    };
 
     /**
      * Length of SMS-CB header
@@ -87,6 +123,8 @@
 
     private final int mFormat;
 
+    private DataCodingScheme mDataCodingSchemeStructedData;
+
     /** ETWS warning notification info. */
     private final SmsCbEtwsInfo mEtwsInfo;
 
@@ -166,6 +204,10 @@
             mNrOfPages = 1;
         }
 
+        if (mDataCodingScheme != -1) {
+            mDataCodingSchemeStructedData = new DataCodingScheme(mDataCodingScheme);
+        }
+
         if (isEtwsMessage()) {
             boolean emergencyUserAlert = isEtwsEmergencyUserAlert();
             boolean activatePopup = isEtwsPopupAlert();
@@ -206,6 +248,10 @@
         return mDataCodingScheme;
     }
 
+    DataCodingScheme getDataCodingSchemeStructedData() {
+        return mDataCodingSchemeStructedData;
+    }
+
     @UnsupportedAppUsage
     int getPageIndex() {
         return mPageIndex;
@@ -457,4 +503,93 @@
                 + ", DCS=0x" + Integer.toHexString(mDataCodingScheme)
                 + ", page " + mPageIndex + " of " + mNrOfPages + '}';
     }
-}
\ No newline at end of file
+
+    /**
+     * CBS Data Coding Scheme.
+     * Reference: 3GPP TS 23.038 version 15.0.0 section #5, CBS Data Coding Scheme
+     */
+    public static final class DataCodingScheme {
+        public final int encoding;
+        public final String language;
+        public final boolean hasLanguageIndicator;
+
+        public DataCodingScheme(int dataCodingScheme) {
+            int encoding = 0;
+            String language = null;
+            boolean hasLanguageIndicator = false;
+
+            // Extract encoding and language from DCS, as defined in 3gpp TS 23.038,
+            // section 5.
+            switch ((dataCodingScheme & 0xf0) >> 4) {
+                case 0x00:
+                    encoding = SmsConstants.ENCODING_7BIT;
+                    language = LANGUAGE_CODES_GROUP_0[dataCodingScheme & 0x0f];
+                    break;
+
+                case 0x01:
+                    hasLanguageIndicator = true;
+                    if ((dataCodingScheme & 0x0f) == 0x01) {
+                        encoding = SmsConstants.ENCODING_16BIT;
+                    } else {
+                        encoding = SmsConstants.ENCODING_7BIT;
+                    }
+                    break;
+
+                case 0x02:
+                    encoding = SmsConstants.ENCODING_7BIT;
+                    language = LANGUAGE_CODES_GROUP_2[dataCodingScheme & 0x0f];
+                    break;
+
+                case 0x03:
+                    encoding = SmsConstants.ENCODING_7BIT;
+                    break;
+
+                case 0x04:
+                case 0x05:
+                    switch ((dataCodingScheme & 0x0c) >> 2) {
+                        case 0x01:
+                            encoding = SmsConstants.ENCODING_8BIT;
+                            break;
+
+                        case 0x02:
+                            encoding = SmsConstants.ENCODING_16BIT;
+                            break;
+
+                        case 0x00:
+                        default:
+                            encoding = SmsConstants.ENCODING_7BIT;
+                            break;
+                    }
+                    break;
+
+                case 0x06:
+                case 0x07:
+                    // Compression not supported
+                case 0x09:
+                    // UDH structure not supported
+                case 0x0e:
+                    // Defined by the WAP forum not supported
+                    throw new IllegalArgumentException("Unsupported GSM dataCodingScheme "
+                            + dataCodingScheme);
+
+                case 0x0f:
+                    if (((dataCodingScheme & 0x04) >> 2) == 0x01) {
+                        encoding = SmsConstants.ENCODING_8BIT;
+                    } else {
+                        encoding = SmsConstants.ENCODING_7BIT;
+                    }
+                    break;
+
+                default:
+                    // Reserved values are to be treated as 7-bit
+                    encoding = SmsConstants.ENCODING_7BIT;
+                    break;
+            }
+
+
+            this.encoding = encoding;
+            this.language = language;
+            this.hasLanguageIndicator = hasLanguageIndicator;
+        }
+    }
+}
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 0129c4c..9a653cf 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -26,7 +26,6 @@
     ],
 
     srcs_lib: "framework-minus-apex",
-    srcs_lib_whitelist_dirs: ["core/java"],
     srcs_lib_whitelist_pkgs: ["android"],
     compile_dex: true,
 }
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
index ca1eb70..ef973ac 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
@@ -63,6 +63,8 @@
     @Mock
     private UsbSettingsManager mUsbSettingsManager;
     @Mock
+    private UsbPermissionManager mUsbPermissionManager;
+    @Mock
     private SharedPreferences mSharedPreferences;
     @Mock
     private SharedPreferences.Editor mEditor;
@@ -87,8 +89,9 @@
         Intent mBroadcastedIntent;
 
         MockUsbHandler(Looper looper, Context context, UsbDeviceManager deviceManager,
-                UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
-            super(looper, context, deviceManager, alsaManager, settingsManager);
+                UsbAlsaManager alsaManager, UsbSettingsManager settingsManager,
+                UsbPermissionManager permissionManager) {
+            super(looper, context, deviceManager, alsaManager, permissionManager);
             mUseUsbNotification = false;
             mIsUsbTransferAllowed = true;
             mCurrentUsbFunctionsReceived = true;
@@ -142,7 +145,7 @@
 
         mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
                 InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbAlsaManager,
-                mUsbSettingsManager);
+                mUsbSettingsManager, mUsbPermissionManager);
     }
 
     @SmallTest
@@ -205,7 +208,7 @@
                 UsbManager.USB_FUNCTION_ADB);
         mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
                 InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbAlsaManager,
-                mUsbSettingsManager);
+                mUsbSettingsManager, mUsbPermissionManager);
 
         sendBootCompleteMessages(mUsbHandler);
         mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_ENABLE_ADB, 0));
@@ -228,7 +231,7 @@
         mMockProperties.put(UsbDeviceManager.UsbHandler.USB_PERSISTENT_CONFIG_PROPERTY, "adb");
         mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
                 InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbAlsaManager,
-                mUsbSettingsManager);
+                mUsbSettingsManager, mUsbPermissionManager);
 
         sendBootCompleteMessages(mUsbHandler);
         assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
@@ -316,7 +319,7 @@
                 .thenReturn(UsbManager.USB_FUNCTION_MTP);
         mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
                 InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbAlsaManager,
-                mUsbSettingsManager);
+                mUsbSettingsManager, mUsbPermissionManager);
         sendBootCompleteMessages(mUsbHandler);
         mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_SCREEN_LOCK, 1));
         mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_SCREEN_LOCK, 0));
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ca65736..e9c24cd 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -70,7 +70,6 @@
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -1170,15 +1169,16 @@
         updateVerboseLoggingEnabledFromService();
     }
 
-    private IWifiManager getIWifiManager() throws RemoteException {
+    private IWifiManager getIWifiManager() {
         if (mService == null) {
             synchronized (this) {
                 mService = IWifiManager.Stub.asInterface(
                         ServiceManager.getService(Context.WIFI_SERVICE));
-                if (mService == null) {
-                    throw new RemoteException("Wifi Service not running");
+                if (mService != null) {
+                    updateVerboseLoggingEnabledFromService();
+                } else {
+                    Log.e(TAG, "Wifi Service not running yet, ignoring WifiManager API call");
                 }
-                updateVerboseLoggingEnabledFromService();
             }
         }
         return mService;
@@ -1223,8 +1223,10 @@
     @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE})
     public List<WifiConfiguration> getConfiguredNetworks() {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
             ParceledListSlice<WifiConfiguration> parceledList =
-                    getIWifiManager().getConfiguredNetworks(mContext.getOpPackageName());
+                    iWifiManager.getConfiguredNetworks(mContext.getOpPackageName());
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1239,8 +1241,10 @@
     @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE, READ_WIFI_CREDENTIAL})
     public List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
             ParceledListSlice<WifiConfiguration> parceledList =
-                    getIWifiManager().getPrivilegedConfiguredNetworks(mContext.getOpPackageName());
+                    iWifiManager.getPrivilegedConfiguredNetworks(mContext.getOpPackageName());
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1271,14 +1275,16 @@
             @NonNull List<ScanResult> scanResults) {
         List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> configs = new ArrayList<>();
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
             Map<String, Map<Integer, List<ScanResult>>> results =
-                    getIWifiManager().getAllMatchingFqdnsForScanResults(
+                    iWifiManager.getAllMatchingFqdnsForScanResults(
                             scanResults);
             if (results.isEmpty()) {
                 return configs;
             }
             List<WifiConfiguration> wifiConfigurations =
-                    getIWifiManager().getWifiConfigsForPasspointProfiles(
+                    iWifiManager.getWifiConfigsForPasspointProfiles(
                             new ArrayList<>(results.keySet()));
             for (WifiConfiguration configuration : wifiConfigurations) {
                 Map<Integer, List<ScanResult>> scanResultsPerNetworkType = results.get(
@@ -1313,10 +1319,12 @@
     public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
             @Nullable List<ScanResult> scanResults) {
         if (scanResults == null) {
-            return new HashMap<>();
+            return Collections.emptyMap();
         }
         try {
-            return getIWifiManager().getMatchingOsuProviders(scanResults);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyMap();
+            return iWifiManager.getMatchingOsuProviders(scanResults);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1343,7 +1351,9 @@
     public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(
             @NonNull Set<OsuProvider> osuProviders) {
         try {
-            return getIWifiManager().getMatchingPasspointConfigsForOsuProviders(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyMap();
+            return iWifiManager.getMatchingPasspointConfigsForOsuProviders(
                     new ArrayList<>(osuProviders));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1428,7 +1438,9 @@
      */
     private int addOrUpdateNetwork(WifiConfiguration config) {
         try {
-            return getIWifiManager().addOrUpdateNetwork(config, mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return -1;
+            return iWifiManager.addOrUpdateNetwork(config, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1654,7 +1666,11 @@
         Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
-            getIWifiManager().registerNetworkRequestMatchCallback(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.registerNetworkRequestMatchCallback(
                     binder, new NetworkRequestMatchCallbackProxy(looper, callback),
                     callback.hashCode());
         } catch (RemoteException e) {
@@ -1680,7 +1696,11 @@
         Log.v(TAG, "unregisterNetworkRequestMatchCallback: callback=" + callback);
 
         try {
-            getIWifiManager().unregisterNetworkRequestMatchCallback(callback.hashCode());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.unregisterNetworkRequestMatchCallback(callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1714,7 +1734,9 @@
     public @NetworkSuggestionsStatusCode int addNetworkSuggestions(
             @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
         try {
-            return getIWifiManager().addNetworkSuggestions(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL;
+            return iWifiManager.addNetworkSuggestions(
                     networkSuggestions, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1738,7 +1760,9 @@
     public @NetworkSuggestionsStatusCode int removeNetworkSuggestions(
             @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
         try {
-            return getIWifiManager().removeNetworkSuggestions(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL;
+            return iWifiManager.removeNetworkSuggestions(
                     networkSuggestions, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1754,7 +1778,9 @@
     @RequiresPermission(ACCESS_WIFI_STATE)
     public @NonNull List<WifiNetworkSuggestion> getNetworkSuggestions() {
         try {
-            return mService.getNetworkSuggestions(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
+            return iWifiManager.getNetworkSuggestions(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -1784,7 +1810,11 @@
      */
     public void addOrUpdatePasspointConfiguration(PasspointConfiguration config) {
         try {
-            if (!getIWifiManager().addOrUpdatePasspointConfiguration(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            if (!iWifiManager.addOrUpdatePasspointConfiguration(
                     config, mContext.getOpPackageName())) {
                 throw new IllegalArgumentException();
             }
@@ -1808,7 +1838,11 @@
     })
     public void removePasspointConfiguration(String fqdn) {
         try {
-            if (!getIWifiManager().removePasspointConfiguration(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            if (!iWifiManager.removePasspointConfiguration(
                     fqdn, mContext.getOpPackageName())) {
                 throw new IllegalArgumentException();
             }
@@ -1832,7 +1866,9 @@
     })
     public List<PasspointConfiguration> getPasspointConfigurations() {
         try {
-            return getIWifiManager().getPasspointConfigurations(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) Collections.emptyList();
+            return iWifiManager.getPasspointConfigurations(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1852,7 +1888,11 @@
      */
     public void queryPasspointIcon(long bssid, String fileName) {
         try {
-            getIWifiManager().queryPasspointIcon(bssid, fileName);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.queryPasspointIcon(bssid, fileName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1866,7 +1906,9 @@
      */
     public int matchProviderWithCurrentNetwork(String fqdn) {
         try {
-            return getIWifiManager().matchProviderWithCurrentNetwork(fqdn);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return -1;
+            return iWifiManager.matchProviderWithCurrentNetwork(fqdn);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1880,7 +1922,11 @@
      */
     public void deauthenticateNetwork(long holdoff, boolean ess) {
         try {
-            getIWifiManager().deauthenticateNetwork(holdoff, ess);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.deauthenticateNetwork(holdoff, ess);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1910,7 +1956,9 @@
     @Deprecated
     public boolean removeNetwork(int netId) {
         try {
-            return getIWifiManager().removeNetwork(netId, mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.removeNetwork(netId, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1965,7 +2013,9 @@
 
         boolean success;
         try {
-            success = getIWifiManager().enableNetwork(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            success = iWifiManager.enableNetwork(
                     netId, attemptConnect, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2002,7 +2052,9 @@
     @Deprecated
     public boolean disableNetwork(int netId) {
         try {
-            return getIWifiManager().disableNetwork(netId, mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.disableNetwork(netId, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2025,7 +2077,9 @@
     @Deprecated
     public boolean disconnect() {
         try {
-            return getIWifiManager().disconnect(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.disconnect(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2049,7 +2103,9 @@
     @Deprecated
     public boolean reconnect() {
         try {
-            return getIWifiManager().reconnect(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.reconnect(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2073,7 +2129,9 @@
     @Deprecated
     public boolean reassociate() {
         try {
-            return getIWifiManager().reassociate(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.reassociate(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2159,7 +2217,9 @@
 
     private long getSupportedFeatures() {
         try {
-            return getIWifiManager().getSupportedFeatures();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return 0L;
+            return iWifiManager.getSupportedFeatures();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2289,8 +2349,10 @@
      */
     public WifiActivityEnergyInfo getControllerActivityEnergyInfo() {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
             synchronized(this) {
-                return getIWifiManager().reportActivityInfo();
+                return iWifiManager.reportActivityInfo();
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2329,8 +2391,10 @@
     @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
     public boolean startScan(WorkSource workSource) {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
             String packageName = mContext.getOpPackageName();
-            return getIWifiManager().startScan(packageName);
+            return iWifiManager.startScan(packageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2359,7 +2423,9 @@
      */
     public WifiInfo getConnectionInfo() {
         try {
-            return getIWifiManager().getConnectionInfo(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getConnectionInfo(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2373,7 +2439,9 @@
      */
     public List<ScanResult> getScanResults() {
         try {
-            return getIWifiManager().getScanResults(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return Collections.emptyList();
+            return iWifiManager.getScanResults(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2392,7 +2460,9 @@
     @Deprecated
     public boolean isScanAlwaysAvailable() {
         try {
-            return getIWifiManager().isScanAlwaysAvailable();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.isScanAlwaysAvailable();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2423,7 +2493,11 @@
      */
     public void setCountryCode(@NonNull String country) {
         try {
-            getIWifiManager().setCountryCode(country);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.setCountryCode(country);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2438,7 +2512,9 @@
     @UnsupportedAppUsage
     public String getCountryCode() {
         try {
-            String country = getIWifiManager().getCountryCode();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            String country = iWifiManager.getCountryCode();
             return country;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2453,7 +2529,9 @@
     @UnsupportedAppUsage
     public boolean isDualBandSupported() {
         try {
-            return getIWifiManager().isDualBandSupported();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.isDualBandSupported();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2466,7 +2544,9 @@
      */
     public boolean isDualModeSupported() {
         try {
-            return getIWifiManager().needs5GHzToAnyApBandConversion();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.needs5GHzToAnyApBandConversion();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2479,7 +2559,9 @@
      */
     public DhcpInfo getDhcpInfo() {
         try {
-            return getIWifiManager().getDhcpInfo();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getDhcpInfo();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2506,7 +2588,9 @@
     @Deprecated
     public boolean setWifiEnabled(boolean enabled) {
         try {
-            return getIWifiManager().setWifiEnabled(mContext.getOpPackageName(), enabled);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.setWifiEnabled(mContext.getOpPackageName(), enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2521,7 +2605,9 @@
      */
     public int getWifiState() {
         try {
-            return getIWifiManager().getWifiEnabledState();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return WIFI_STATE_UNKNOWN;
+            return iWifiManager.getWifiEnabledState();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2595,7 +2681,11 @@
      */
     public void updateInterfaceIpState(String ifaceName, int mode) {
         try {
-            getIWifiManager().updateInterfaceIpState(ifaceName, mode);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.updateInterfaceIpState(ifaceName, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2613,7 +2703,9 @@
      */
     public boolean startSoftAp(@Nullable WifiConfiguration wifiConfig) {
         try {
-            return getIWifiManager().startSoftAp(wifiConfig);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.startSoftAp(wifiConfig);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2628,7 +2720,9 @@
      */
     public boolean stopSoftAp() {
         try {
-            return getIWifiManager().stopSoftAp();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.stopSoftAp();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2696,8 +2790,12 @@
             LocalOnlyHotspotCallbackProxy proxy =
                     new LocalOnlyHotspotCallbackProxy(this, looper, callback);
             try {
+                IWifiManager iWifiManager = getIWifiManager();
+                if (iWifiManager == null) {
+                    throw new RemoteException("Wifi service is not running");
+                }
                 String packageName = mContext.getOpPackageName();
-                int returnCode = getIWifiManager().startLocalOnlyHotspot(
+                int returnCode = iWifiManager.startLocalOnlyHotspot(
                         proxy.getMessenger(), new Binder(), packageName);
                 if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) {
                     // Send message to the proxy to make sure we call back on the correct thread
@@ -2749,7 +2847,11 @@
             }
             mLOHSCallbackProxy = null;
             try {
-                getIWifiManager().stopLocalOnlyHotspot();
+                IWifiManager iWifiManager = getIWifiManager();
+                if (iWifiManager == null) {
+                    throw new RemoteException("Wifi service is not running");
+                }
+                iWifiManager.stopLocalOnlyHotspot();
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -2779,7 +2881,11 @@
             Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
             mLOHSObserverProxy = new LocalOnlyHotspotObserverProxy(this, looper, observer);
             try {
-                getIWifiManager().startWatchLocalOnlyHotspot(
+                IWifiManager iWifiManager = getIWifiManager();
+                if (iWifiManager == null) {
+                    throw new RemoteException("Wifi service is not running");
+                }
+                iWifiManager.startWatchLocalOnlyHotspot(
                         mLOHSObserverProxy.getMessenger(), new Binder());
                 mLOHSObserverProxy.registered();
             } catch (RemoteException e) {
@@ -2803,7 +2909,11 @@
             }
             mLOHSObserverProxy = null;
             try {
-                getIWifiManager().stopWatchLocalOnlyHotspot();
+                IWifiManager iWifiManager = getIWifiManager();
+                if (iWifiManager == null) {
+                    throw new RemoteException("Wifi service is not running");
+                }
+                iWifiManager.stopWatchLocalOnlyHotspot();
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -2823,7 +2933,9 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
     public int getWifiApState() {
         try {
-            return getIWifiManager().getWifiApEnabledState();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return WIFI_AP_STATE_FAILED;
+            return iWifiManager.getWifiApEnabledState();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2852,7 +2964,9 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
     public WifiConfiguration getWifiApConfiguration() {
         try {
-            return getIWifiManager().getWifiApConfiguration();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getWifiApConfiguration();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2869,7 +2983,9 @@
     @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
     public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
         try {
-            return getIWifiManager().setWifiApConfiguration(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.setWifiApConfiguration(
                     wifiConfig, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2885,7 +3001,11 @@
     public void notifyUserOfApBandConversion() {
         Log.d(TAG, "apBand was converted, notify the user");
         try {
-            getIWifiManager().notifyUserOfApBandConversion(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.notifyUserOfApBandConversion(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2913,7 +3033,11 @@
      */
     public void setTdlsEnabled(InetAddress remoteIPAddress, boolean enable) {
         try {
-            getIWifiManager().enableTdls(remoteIPAddress.getHostAddress(), enable);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.enableTdls(remoteIPAddress.getHostAddress(), enable);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2927,7 +3051,11 @@
      */
     public void setTdlsEnabledWithMacAddress(String remoteMacAddress, boolean enable) {
         try {
-            getIWifiManager().enableTdlsWithMacAddress(remoteMacAddress, enable);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.enableTdlsWithMacAddress(remoteMacAddress, enable);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3216,7 +3344,11 @@
         Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
-            getIWifiManager().registerSoftApCallback(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.registerSoftApCallback(
                     binder, new SoftApCallbackProxy(looper, callback), callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -3237,7 +3369,11 @@
         Log.v(TAG, "unregisterSoftApCallback: callback=" + callback);
 
         try {
-            getIWifiManager().unregisterSoftApCallback(callback.hashCode());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.unregisterSoftApCallback(callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3807,7 +3943,11 @@
     public void disableEphemeralNetwork(String SSID) {
         if (SSID == null) throw new IllegalArgumentException("SSID cannot be null");
         try {
-            getIWifiManager().disableEphemeralNetwork(SSID, mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.disableEphemeralNetwork(SSID, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3851,7 +3991,9 @@
     @UnsupportedAppUsage
     private Messenger getWifiServiceMessenger() {
         try {
-            return getIWifiManager().getWifiServiceMessenger(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getWifiServiceMessenger(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3911,10 +4053,14 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (++mRefCount == 1) : (!mHeld)) {
                     try {
-                        getIWifiManager().acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
                         synchronized (WifiManager.this) {
                             if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
-                                getIWifiManager().releaseWifiLock(mBinder);
+                                iWifiManager.releaseWifiLock(mBinder);
                                 throw new UnsupportedOperationException(
                                             "Exceeded maximum number of wifi locks");
                             }
@@ -3944,7 +4090,11 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
                     try {
-                        getIWifiManager().releaseWifiLock(mBinder);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.releaseWifiLock(mBinder);
                         synchronized (WifiManager.this) {
                             mActiveLockCount--;
                         }
@@ -4007,7 +4157,11 @@
                 }
                 if (changed && mHeld) {
                     try {
-                        getIWifiManager().updateWifiLockWorkSource(mBinder, mWorkSource);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.updateWifiLockWorkSource(mBinder, mWorkSource);
                     } catch (RemoteException e) {
                         throw e.rethrowFromSystemServer();
                     }
@@ -4035,7 +4189,11 @@
             synchronized (mBinder) {
                 if (mHeld) {
                     try {
-                        getIWifiManager().releaseWifiLock(mBinder);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.releaseWifiLock(mBinder);
                         synchronized (WifiManager.this) {
                             mActiveLockCount--;
                         }
@@ -4148,10 +4306,14 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (++mRefCount == 1) : (!mHeld)) {
                     try {
-                        getIWifiManager().acquireMulticastLock(mBinder, mTag);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.acquireMulticastLock(mBinder, mTag);
                         synchronized (WifiManager.this) {
                             if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
-                                getIWifiManager().releaseMulticastLock(mTag);
+                                iWifiManager.releaseMulticastLock(mTag);
                                 throw new UnsupportedOperationException(
                                         "Exceeded maximum number of wifi locks");
                             }
@@ -4193,7 +4355,11 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
                     try {
-                        getIWifiManager().releaseMulticastLock(mTag);
+                        IWifiManager iWifiManager = getIWifiManager();
+                        if (iWifiManager == null) {
+                            throw new RemoteException("Wifi service is not running");
+                        }
+                        iWifiManager.releaseMulticastLock(mTag);
                         synchronized (WifiManager.this) {
                             mActiveLockCount--;
                         }
@@ -4270,7 +4436,9 @@
      */
     public boolean isMulticastEnabled() {
         try {
-            return getIWifiManager().isMulticastEnabled();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            return iWifiManager.isMulticastEnabled();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4283,7 +4451,9 @@
     @UnsupportedAppUsage
     public boolean initializeMulticastFiltering() {
         try {
-            getIWifiManager().initializeMulticastFiltering();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return false;
+            iWifiManager.initializeMulticastFiltering();
             return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4308,7 +4478,11 @@
     @UnsupportedAppUsage
     public void enableVerboseLogging (int verbose) {
         try {
-            getIWifiManager().enableVerboseLogging(verbose);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.enableVerboseLogging(verbose);
         } catch (Exception e) {
             //ignore any failure here
             Log.e(TAG, "enableVerboseLogging " + e.toString());
@@ -4323,7 +4497,9 @@
     @UnsupportedAppUsage
     public int getVerboseLoggingLevel() {
         try {
-            return getIWifiManager().getVerboseLoggingLevel();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return -1;
+            return iWifiManager.getVerboseLoggingLevel();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4336,7 +4512,11 @@
      */
     public void factoryReset() {
         try {
-            getIWifiManager().factoryReset(mContext.getOpPackageName());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.factoryReset(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4350,7 +4530,9 @@
     @UnsupportedAppUsage
     public Network getCurrentNetwork() {
         try {
-            return getIWifiManager().getCurrentNetwork();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getCurrentNetwork();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4382,7 +4564,11 @@
      */
     public void enableWifiConnectivityManager(boolean enabled) {
         try {
-            getIWifiManager().enableWifiConnectivityManager(enabled);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.enableWifiConnectivityManager(enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4394,7 +4580,9 @@
      */
     public byte[] retrieveBackupData() {
         try {
-            return getIWifiManager().retrieveBackupData();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.retrieveBackupData();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4406,7 +4594,11 @@
      */
     public void restoreBackupData(byte[] data) {
         try {
-            getIWifiManager().restoreBackupData(data);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.restoreBackupData(data);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4422,7 +4614,11 @@
     @Deprecated
     public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
         try {
-            getIWifiManager().restoreSupplicantBackupData(supplicantData, ipConfigData);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.restoreSupplicantBackupData(supplicantData, ipConfigData);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4451,7 +4647,11 @@
             throw new IllegalArgumentException("callback must not be null");
         }
         try {
-            getIWifiManager().startSubscriptionProvisioning(provider,
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.startSubscriptionProvisioning(provider,
                     new ProvisioningCallbackProxy(executor, callback));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4565,7 +4765,11 @@
         Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
-            getIWifiManager().registerTrafficStateCallback(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.registerTrafficStateCallback(
                     binder, new TrafficStateCallbackProxy(looper, callback), callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4585,7 +4789,11 @@
         Log.v(TAG, "unregisterTrafficStateCallback: callback=" + callback);
 
         try {
-            getIWifiManager().unregisterTrafficStateCallback(callback.hashCode());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.unregisterTrafficStateCallback(callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4641,7 +4849,9 @@
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public String[] getFactoryMacAddresses() {
         try {
-            return getIWifiManager().getFactoryMacAddresses();
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) return null;
+            return iWifiManager.getFactoryMacAddresses();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4710,7 +4920,11 @@
     @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE)
     public void setDeviceMobilityState(@DeviceMobilityState int state) {
         try {
-            getIWifiManager().setDeviceMobilityState(state);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.setDeviceMobilityState(state);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4765,7 +4979,11 @@
             @NonNull EasyConnectStatusCallback callback) {
         Binder binder = new Binder();
         try {
-            getIWifiManager().startDppAsConfiguratorInitiator(
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.startDppAsConfiguratorInitiator(
                     binder, enrolleeUri, selectedNetworkId, enrolleeNetworkRole,
                     new EasyConnectCallbackProxy(executor, callback));
         } catch (RemoteException e) {
@@ -4792,7 +5010,11 @@
             @NonNull EasyConnectStatusCallback callback) {
         Binder binder = new Binder();
         try {
-            getIWifiManager().startDppAsEnrolleeInitiator(binder, configuratorUri,
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.startDppAsEnrolleeInitiator(binder, configuratorUri,
                     new EasyConnectCallbackProxy(executor, callback));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4813,8 +5035,12 @@
             android.Manifest.permission.NETWORK_SETUP_WIZARD})
     public void stopEasyConnectSession() {
         try {
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
             /* Request lower layers to stop/abort and clear resources */
-            getIWifiManager().stopDppSession();
+            iWifiManager.stopDppSession();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4913,7 +5139,11 @@
             Log.v(TAG, "addOnWifiUsabilityStatsListener: listener=" + listener);
         }
         try {
-            getIWifiManager().addOnWifiUsabilityStatsListener(new Binder(),
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.addOnWifiUsabilityStatsListener(new Binder(),
                     new IOnWifiUsabilityStatsListener.Stub() {
                         @Override
                         public void onWifiUsabilityStats(int seqNum, boolean isSameBssidAndFreq,
@@ -4950,7 +5180,11 @@
             Log.v(TAG, "removeOnWifiUsabilityStatsListener: listener=" + listener);
         }
         try {
-            getIWifiManager().removeOnWifiUsabilityStatsListener(listener.hashCode());
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.removeOnWifiUsabilityStatsListener(listener.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4973,7 +5207,11 @@
     @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE)
     public void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec) {
         try {
-            getIWifiManager().updateWifiUsabilityScore(seqNum, score, predictionHorizonSec);
+            IWifiManager iWifiManager = getIWifiManager();
+            if (iWifiManager == null) {
+                throw new RemoteException("Wifi service is not running");
+            }
+            iWifiManager.updateWifiUsabilityScore(seqNum, score, predictionHorizonSec);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }