Merge "Make ModemActivityInfo isValid system"
diff --git a/Android.bp b/Android.bp
index a26135e..21552695 100644
--- a/Android.bp
+++ b/Android.bp
@@ -200,34 +200,9 @@
 }
 
 filegroup {
-    name: "framework-wifi-sources",
-    srcs: [
-        "wifi/java/**/*.java",
-        "wifi/java/**/*.aidl",
-    ],
-    exclude_srcs: [
-        ":framework-wifi-non-updatable-sources"
-    ],
-    path: "wifi/java",
-}
-
-filegroup {
-    name: "framework-wifi-non-updatable-sources",
-    srcs: [
-        // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and
-        // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
-        // to a separate package.
-        "wifi/java/android/net/wifi/WifiNetworkScoreCache.java",
-        "wifi/java/android/net/wifi/WifiCondManager.java",
-        "wifi/java/android/net/wifi/wificond/*.java",
-    ],
-}
-
-filegroup {
     name: "framework-non-updatable-sources",
     srcs: [
         // Java/AIDL sources under frameworks/base
-        ":framework-appsearch-sources",
         ":framework-blobstore-sources",
         ":framework-core-sources",
         ":framework-drm-sources",
@@ -247,9 +222,8 @@
         ":framework-telecomm-sources",
         ":framework-telephony-common-sources",
         ":framework-telephony-sources",
-        ":framework-wifi-sources",
+        ":framework-wifi-annotations",
         ":framework-wifi-non-updatable-sources",
-	":libwificond_ipc_aidl",
         ":PacProcessor-aidl-sources",
         ":ProxyHandler-aidl-sources",
 
@@ -287,9 +261,11 @@
 filegroup {
     name: "framework-updatable-sources",
     srcs: [
+        ":framework-appsearch-sources",
         ":framework-sdkext-sources",
         ":framework-statsd-sources",
         ":updatable-media-srcs",
+        ":framework-wifi-updatable-sources",
     ]
 }
 
@@ -425,7 +401,7 @@
 
 filegroup {
     name: "framework-jarjar-rules",
-    srcs: ["jarjar_rules_hidl.txt"],
+    srcs: ["framework-jarjar-rules.txt"],
 }
 
 filegroup {
@@ -451,8 +427,10 @@
     defaults: ["framework-defaults"],
     srcs: [":framework-non-updatable-sources"],
     libs: [
+        "framework-appsearch-stubs",
         // TODO(b/146167933): Use framework-statsd-stubs
         "framework-statsd",
+        "framework-wifi-stubs",
     ],
     installable: true,
     javac_shard_size: 150,
@@ -489,9 +467,12 @@
     installable: false, // this lib is a build-only library
     static_libs: [
         "framework-minus-apex",
+        "framework-appsearch", // TODO(b/146218515): should be framework-appsearch-stubs
         "framework-sdkext-stubs-systemapi",
         // TODO(b/146167933): Use framework-statsd-stubs instead.
         "framework-statsd",
+        // TODO(b/140299412): should be framework-wifi-stubs
+        "framework-wifi",
         // TODO(jiyong): add more stubs for APEXes here
     ],
     sdk_version: "core_platform",
diff --git a/apex/Android.bp b/apex/Android.bp
index 9ea3953..56f7db2 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -26,6 +26,9 @@
     "--hide Typo " +
     "--hide UnavailableSymbol "
 
+// TODO: remove this server classes are cleaned up.
+mainline_stubs_args += "--hide-package com.android.server "
+
 stubs_defaults {
     name: "framework-module-stubs-defaults-publicapi",
     args: mainline_stubs_args,
@@ -34,6 +37,6 @@
 
 stubs_defaults {
     name: "framework-module-stubs-defaults-systemapi",
-    args: mainline_stubs_args + " --show-annotation android.annotation.SystemApi ",
+    args: mainline_stubs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) ",
     installable: false,
 }
diff --git a/apex/appsearch/Android.bp b/apex/appsearch/Android.bp
index bcdcc7d..b014fdc 100644
--- a/apex/appsearch/Android.bp
+++ b/apex/appsearch/Android.bp
@@ -14,9 +14,11 @@
 
 apex {
     name: "com.android.appsearch",
-
     manifest: "apex_manifest.json",
-
+    java_libs: [
+        "framework-appsearch",
+        "service-appsearch",
+    ],
     key: "com.android.appsearch.key",
     certificate: ":com.android.appsearch.certificate",
 }
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index 0a65f73..3dc5a2c 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -23,17 +23,52 @@
 
 java_library {
   name: "framework-appsearch",
-  installable: false,
-  sdk_version: "core_platform",
-  srcs: [
-    ":framework-appsearch-sources",
-  ],
-  aidl: {
-    export_include_dirs: [
-      "java",
-    ],
-  },
+  installable: true,
+  sdk_version: "core_platform", // TODO(b/146218515) should be core_current
+  srcs: [":framework-appsearch-sources"],
   libs: [
-    "framework-minus-apex",
+    "framework-minus-apex",  // TODO(b/146218515) should be framework-system-stubs
   ],
 }
+
+metalava_appsearch_docs_args =
+    "--hide-package com.android.server " +
+    "--error UnhiddenSystemApi " +
+    "--hide RequiresPermission " +
+    "--hide MissingPermission " +
+    "--hide BroadcastBehavior " +
+    "--hide HiddenSuperclass " +
+    "--hide DeprecationMismatch " +
+    "--hide UnavailableSymbol " +
+    "--hide SdkConstant " +
+    "--hide HiddenTypeParameter " +
+    "--hide Todo --hide Typo " +
+    "--hide HiddenTypedefConstant " +
+    "--show-annotation android.annotation.SystemApi "
+
+droidstubs {
+    name: "framework-appsearch-stubs-srcs",
+    srcs: [
+        ":framework-annotations",
+        ":framework-appsearch-sources",
+    ],
+    aidl: {
+        include_dirs: ["frameworks/base/core/java"],
+    },
+    args: metalava_appsearch_docs_args,
+    sdk_version: "core_current",
+    libs: ["android_system_stubs_current"],
+}
+
+java_library {
+    name: "framework-appsearch-stubs",
+    srcs: [":framework-appsearch-stubs-srcs"],
+    aidl: {
+        export_include_dirs: [
+            "java",
+        ],
+    },
+    sdk_version: "core_current",
+    libs: ["android_system_stubs_current"],
+    installable: false,
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java
index fcebe3d..02cc967 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java
@@ -15,19 +15,25 @@
  */
 package android.app.appsearch;
 
+import android.annotation.SystemApi;
 import android.app.SystemServiceRegistry;
 import android.content.Context;
 
 /**
- * This is where the AppSearchManagerService wrapper is registered.
+ * Class holding initialization code for the AppSearch module.
  *
- * TODO(b/142567528): add comments when implement this class
  * @hide
  */
+@SystemApi
 public class AppSearchManagerFrameworkInitializer {
+    private AppSearchManagerFrameworkInitializer() {}
 
     /**
-     * TODO(b/142567528): add comments when implement this class
+     * Called by {@link SystemServiceRegistry}'s static initializer and registers all AppSearch
+     * services to {@link Context}, so that {@link Context#getSystemService} can return them.
+     *
+     * @throws IllegalStateException if this is called from anywhere besides
+     *     {@link SystemServiceRegistry}
      */
     public static void initialize() {
         SystemServiceRegistry.registerStaticService(
diff --git a/apex/sdkext/Android.bp b/apex/sdkext/Android.bp
index b9071f8..5369a96 100644
--- a/apex/sdkext/Android.bp
+++ b/apex/sdkext/Android.bp
@@ -23,6 +23,7 @@
     java_libs: [ "framework-sdkext" ],
     prebuilts: [
       "com.android.sdkext.ldconfig",
+      "cur_sdkinfo",
       "derive_sdk.rc",
     ],
     key: "com.android.sdkext.key",
@@ -51,3 +52,33 @@
     filename: "ld.config.txt",
     installable: false,
 }
+
+python_binary_host {
+    name: "gen_sdkinfo",
+    srcs: [
+        "sdk.proto",
+        "gen_sdkinfo.py",
+    ],
+    proto: {
+        canonical_path_from_root: false,
+    },
+    version: {
+        py3: {
+            embedded_launcher: true,
+        },
+    },
+}
+
+gensrcs {
+    name: "cur_sdkinfo_src",
+    srcs: [""],
+    tools: [ "gen_sdkinfo" ],
+    cmd: "$(location) -v 0 -o $(out)",
+}
+
+prebuilt_etc {
+    name: "cur_sdkinfo",
+    src: ":cur_sdkinfo_src",
+    filename: "sdkinfo.binarypb",
+    installable: false,
+}
diff --git a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
index d3b9397..a8a7eff 100644
--- a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
+++ b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
@@ -60,7 +60,7 @@
      */
     public static int getExtensionVersion(@SdkVersion int sdk) {
         if (sdk < VERSION_CODES.R) {
-            throw new IllegalArgumentException();
+            throw new IllegalArgumentException(String.valueOf(sdk) + " does not have extensions");
         }
         return R_EXTENSION_INT;
     }
diff --git a/apex/sdkext/gen_sdkinfo.py b/apex/sdkext/gen_sdkinfo.py
new file mode 100644
index 0000000..5af478b
--- /dev/null
+++ b/apex/sdkext/gen_sdkinfo.py
@@ -0,0 +1,19 @@
+import sdk_pb2
+import sys
+
+if __name__ == '__main__':
+  argv = sys.argv[1:]
+  if not len(argv) == 4 or sorted([argv[0], argv[2]]) != ['-o', '-v']:
+    print('usage: gen_sdkinfo -v <version> -o <output-file>')
+    sys.exit(1)
+
+  for i in range(len(argv)):
+    if sys.argv[i] == '-o':
+      filename = sys.argv[i+1]
+    if sys.argv[i] == '-v':
+      version = int(sys.argv[i+1])
+
+  proto = sdk_pb2.SdkVersion()
+  proto.version = version
+  with open(filename, 'wb') as f:
+    f.write(proto.SerializeToString())
diff --git a/apex/sdkext/sdk.proto b/apex/sdkext/sdk.proto
new file mode 100644
index 0000000..d15b935
--- /dev/null
+++ b/apex/sdkext/sdk.proto
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+package com.android.sdkext.proto;
+
+option java_outer_classname = "SdkProto";
+option optimize_for = LITE_RUNTIME;
+
+message SdkVersion {
+  int32 version = 1;
+}
diff --git a/api/current.txt b/api/current.txt
index d15069a..6ddd847 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -17246,6 +17246,7 @@
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME = 12; // 0xc
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10; // 0xa
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING = 15; // 0xf
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING = 4; // 0x4
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
@@ -35028,6 +35029,7 @@
     method public void dump(@NonNull java.io.FileDescriptor, @Nullable String[]) throws android.os.RemoteException;
     method public void dumpAsync(@NonNull java.io.FileDescriptor, @Nullable String[]) throws android.os.RemoteException;
     method @Nullable public String getInterfaceDescriptor() throws android.os.RemoteException;
+    method public static int getSuggestedMaxIpcSizeBytes();
     method public boolean isBinderAlive();
     method public void linkToDeath(@NonNull android.os.IBinder.DeathRecipient, int) throws android.os.RemoteException;
     method public boolean pingBinder();
@@ -35224,6 +35226,7 @@
     method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
     method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
     method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
+    method @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
     method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
     method @Nullable public android.os.PersistableBundle readPersistableBundle();
     method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
@@ -35271,6 +35274,7 @@
     method public void writeNoException();
     method public void writeParcelable(@Nullable android.os.Parcelable, int);
     method public <T extends android.os.Parcelable> void writeParcelableArray(@Nullable T[], int);
+    method public void writeParcelableCreator(@NonNull android.os.Parcelable);
     method public <T extends android.os.Parcelable> void writeParcelableList(@Nullable java.util.List<T>, int);
     method public void writePersistableBundle(@Nullable android.os.PersistableBundle);
     method public void writeSerializable(@Nullable java.io.Serializable);
@@ -38398,11 +38402,11 @@
   protected static interface ContactsContract.RawContactsColumns {
     field public static final String ACCOUNT_TYPE_AND_DATA_SET = "account_type_and_data_set";
     field public static final String AGGREGATION_MODE = "aggregation_mode";
-    field public static final String BACKUP_ID = "backup_id";
+    field @Deprecated public static final String BACKUP_ID = "backup_id";
     field public static final String CONTACT_ID = "contact_id";
     field public static final String DATA_SET = "data_set";
     field public static final String DELETED = "deleted";
-    field public static final String METADATA_DIRTY = "metadata_dirty";
+    field @Deprecated public static final String METADATA_DIRTY = "metadata_dirty";
     field public static final String RAW_CONTACT_IS_READ_ONLY = "raw_contact_is_read_only";
     field public static final String RAW_CONTACT_IS_USER_PROFILE = "raw_contact_is_user_profile";
   }
@@ -38709,6 +38713,10 @@
 
   public final class MediaStore {
     ctor public MediaStore();
+    method @NonNull public static android.app.PendingIntent createDeleteRequest(@NonNull android.content.ContentResolver, @NonNull java.util.Collection<android.net.Uri>);
+    method @NonNull public static android.app.PendingIntent createFavoriteRequest(@NonNull android.content.ContentResolver, @NonNull java.util.Collection<android.net.Uri>, boolean);
+    method @NonNull public static android.app.PendingIntent createTrashRequest(@NonNull android.content.ContentResolver, @NonNull java.util.Collection<android.net.Uri>, boolean);
+    method @NonNull public static android.app.PendingIntent createWriteRequest(@NonNull android.content.ContentResolver, @NonNull java.util.Collection<android.net.Uri>);
     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 android.net.Uri getMediaScannerUri();
@@ -38719,9 +38727,6 @@
     method @NonNull public static String getVolumeName(@NonNull android.net.Uri);
     method @Deprecated @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri);
     method @NonNull public static android.net.Uri setRequireOriginal(@NonNull android.net.Uri);
-    method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri);
-    method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long);
-    method public static void untrash(@NonNull android.content.Context, @NonNull android.net.Uri);
     field public static final String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
     field public static final String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
     field public static final String ACTION_REVIEW = "android.provider.action.REVIEW";
@@ -43891,6 +43896,7 @@
     field public static final String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
     field public static final String EVENT_CALL_REMOTELY_HELD = "android.telecom.event.CALL_REMOTELY_HELD";
     field public static final String EVENT_CALL_REMOTELY_UNHELD = "android.telecom.event.CALL_REMOTELY_UNHELD";
+    field public static final String EVENT_CALL_SWITCH_FAILED = "android.telecom.event.CALL_SWITCH_FAILED";
     field public static final String EVENT_MERGE_COMPLETE = "android.telecom.event.MERGE_COMPLETE";
     field public static final String EVENT_MERGE_START = "android.telecom.event.MERGE_START";
     field public static final String EVENT_ON_HOLD_TONE_END = "android.telecom.event.ON_HOLD_TONE_END";
@@ -44600,6 +44606,7 @@
     method public void notifyConfigChangedForSubId(int);
     field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
     field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
+    field public static final int DATA_CYCLE_USE_PLATFORM_DEFAULT = -1; // 0xffffffff
     field public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX";
     field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
     field public static final String KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrp_thresholds_int_array";
@@ -44610,6 +44617,7 @@
     field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
     field public static final String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
     field public static final String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool";
+    field public static final String KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL = "allow_holding_video_call";
     field public static final String KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL = "allow_hold_call_during_emergency_bool";
     field public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
     field public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool";
@@ -44659,11 +44667,16 @@
     field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string";
     field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string";
     field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
-    field public static final String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
+    field public static final String KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING = "config_ims_mmtel_package_override_string";
+    field @Deprecated public static final String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
+    field public static final String KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING = "config_ims_rcs_package_override_string";
     field public static final String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
     field public static final String KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL = "config_telephony_use_own_number_for_voicemail_bool";
     field public static final String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
+    field public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL = "data_limit_notification_bool";
     field public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
+    field public static final String KEY_DATA_RAPID_NOTIFICATION_BOOL = "data_rapid_notification_bool";
+    field public static final String KEY_DATA_WARNING_NOTIFICATION_BOOL = "data_warning_notification_bool";
     field public static final String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
     field public static final String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
     field public static final String KEY_DEFAULT_VM_NUMBER_ROAMING_AND_IMS_UNREGISTERED_STRING = "default_vm_number_roaming_and_ims_unregistered_string";
@@ -45223,6 +45236,7 @@
     method public void onDataConnectionStateChanged(int);
     method public void onDataConnectionStateChanged(int, int);
     method public void onMessageWaitingIndicatorChanged(boolean);
+    method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
     method public void onServiceStateChanged(android.telephony.ServiceState);
     method @Deprecated public void onSignalStrengthChanged(int);
     method public void onSignalStrengthsChanged(android.telephony.SignalStrength);
@@ -45237,12 +45251,23 @@
     field public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
     field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
     field public static final int LISTEN_NONE = 0; // 0x0
+    field @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
     field public static final int LISTEN_SERVICE_STATE = 1; // 0x1
     field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2
     field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
     field public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000
   }
 
+  public final class PreciseDataConnectionState implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getLastCauseCode();
+    method @Nullable public android.net.LinkProperties getLinkProperties();
+    method public int getNetworkType();
+    method public int getState();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PreciseDataConnectionState> CREATOR;
+  }
+
   public final class RadioAccessSpecifier implements android.os.Parcelable {
     ctor public RadioAccessSpecifier(int, int[], int[]);
     method public int describeContents();
@@ -45755,6 +45780,7 @@
     field public static final int DATA_CONNECTED = 2; // 0x2
     field public static final int DATA_CONNECTING = 1; // 0x1
     field public static final int DATA_DISCONNECTED = 0; // 0x0
+    field public static final int DATA_DISCONNECTING = 4; // 0x4
     field public static final int DATA_SUSPENDED = 3; // 0x3
     field public static final int DATA_UNKNOWN = -1; // 0xffffffff
     field public static final String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
@@ -51081,6 +51107,9 @@
     method public boolean dispatchUnhandledMove(android.view.View, int);
     method protected void dispatchVisibilityChanged(@NonNull android.view.View, int);
     method public void dispatchWindowFocusChanged(boolean);
+    method public void dispatchWindowInsetsAnimationFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
+    method @NonNull public android.view.WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull android.view.WindowInsets);
+    method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds dispatchWindowInsetsAnimationStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds);
     method public void dispatchWindowSystemUiVisiblityChanged(int);
     method public void dispatchWindowVisibilityChanged(int);
     method @CallSuper public void draw(android.graphics.Canvas);
@@ -51261,6 +51290,7 @@
     method @android.view.ViewDebug.ExportedProperty(category="layout") public final int getWidth();
     method protected int getWindowAttachCount();
     method public android.view.WindowId getWindowId();
+    method @Nullable public android.view.WindowInsetsController getWindowInsetsController();
     method public int getWindowSystemUiVisibility();
     method public android.os.IBinder getWindowToken();
     method public int getWindowVisibility();
@@ -51599,6 +51629,7 @@
     method public void setVisibility(int);
     method @Deprecated public void setWillNotCacheDrawing(boolean);
     method public void setWillNotDraw(boolean);
+    method public void setWindowInsetsAnimationCallback(@Nullable android.view.WindowInsetsAnimationCallback);
     method public void setX(float);
     method public void setY(float);
     method public void setZ(float);
@@ -52485,6 +52516,7 @@
     method public android.transition.Transition getExitTransition();
     method protected final int getFeatures();
     method protected final int getForcedWindowFlags();
+    method @Nullable public android.view.WindowInsetsController getInsetsController();
     method @NonNull public abstract android.view.LayoutInflater getLayoutInflater();
     method protected final int getLocalFeatures();
     method public android.media.session.MediaController getMediaController();
@@ -52700,7 +52732,9 @@
     method @NonNull public android.view.WindowInsets consumeStableInsets();
     method @NonNull public android.view.WindowInsets consumeSystemWindowInsets();
     method @Nullable public android.view.DisplayCutout getDisplayCutout();
+    method @NonNull public android.graphics.Insets getInsets(int);
     method @NonNull public android.graphics.Insets getMandatorySystemGestureInsets();
+    method @NonNull public android.graphics.Insets getMaxInsets(int) throws java.lang.IllegalArgumentException;
     method public int getStableInsetBottom();
     method public int getStableInsetLeft();
     method public int getStableInsetRight();
@@ -52719,6 +52753,7 @@
     method @NonNull public android.view.WindowInsets inset(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int);
     method public boolean isConsumed();
     method public boolean isRound();
+    method public boolean isVisible(int);
     method @Deprecated @NonNull public android.view.WindowInsets replaceSystemWindowInsets(int, int, int, int);
     method @Deprecated @NonNull public android.view.WindowInsets replaceSystemWindowInsets(android.graphics.Rect);
   }
@@ -52728,11 +52763,76 @@
     ctor public WindowInsets.Builder(@NonNull android.view.WindowInsets);
     method @NonNull public android.view.WindowInsets build();
     method @NonNull public android.view.WindowInsets.Builder setDisplayCutout(@Nullable android.view.DisplayCutout);
+    method @NonNull public android.view.WindowInsets.Builder setInsets(int, @NonNull android.graphics.Insets);
     method @NonNull public android.view.WindowInsets.Builder setMandatorySystemGestureInsets(@NonNull android.graphics.Insets);
+    method @NonNull public android.view.WindowInsets.Builder setMaxInsets(int, @NonNull android.graphics.Insets) throws java.lang.IllegalArgumentException;
     method @NonNull public android.view.WindowInsets.Builder setStableInsets(@NonNull android.graphics.Insets);
     method @NonNull public android.view.WindowInsets.Builder setSystemGestureInsets(@NonNull android.graphics.Insets);
     method @NonNull public android.view.WindowInsets.Builder setSystemWindowInsets(@NonNull android.graphics.Insets);
     method @NonNull public android.view.WindowInsets.Builder setTappableElementInsets(@NonNull android.graphics.Insets);
+    method @NonNull public android.view.WindowInsets.Builder setVisible(int, boolean);
+  }
+
+  public static final class WindowInsets.Type {
+    method public static int all();
+    method public static int captionBar();
+    method public static int ime();
+    method public static int mandatorySystemGestures();
+    method public static int navigationBars();
+    method public static int statusBars();
+    method public static int systemBars();
+    method public static int systemGestures();
+    method public static int tappableElement();
+    method public static int windowDecor();
+  }
+
+  public interface WindowInsetsAnimationCallback {
+    method public default void onFinished(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation);
+    method @NonNull public android.view.WindowInsets onProgress(@NonNull android.view.WindowInsets);
+    method @NonNull public default android.view.WindowInsetsAnimationCallback.AnimationBounds onStarted(@NonNull android.view.WindowInsetsAnimationCallback.InsetsAnimation, @NonNull android.view.WindowInsetsAnimationCallback.AnimationBounds);
+  }
+
+  public static final class WindowInsetsAnimationCallback.AnimationBounds {
+    ctor public WindowInsetsAnimationCallback.AnimationBounds(@NonNull android.graphics.Insets, @NonNull android.graphics.Insets);
+    method @NonNull public android.graphics.Insets getLowerBound();
+    method @NonNull public android.graphics.Insets getUpperBound();
+    method @NonNull public android.view.WindowInsetsAnimationCallback.AnimationBounds inset(@NonNull android.graphics.Insets);
+  }
+
+  public static final class WindowInsetsAnimationCallback.InsetsAnimation {
+    ctor public WindowInsetsAnimationCallback.InsetsAnimation(int, @Nullable android.view.animation.Interpolator, long);
+    method public long getDurationMillis();
+    method @FloatRange(from=0.0f, to=1.0f) public float getFraction();
+    method public float getInterpolatedFraction();
+    method @Nullable public android.view.animation.Interpolator getInterpolator();
+    method public int getTypeMask();
+    method public void setDuration(long);
+    method public void setFraction(@FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public interface WindowInsetsAnimationControlListener {
+    method public void onCancelled();
+    method public void onReady(@NonNull android.view.WindowInsetsAnimationController, int);
+  }
+
+  public interface WindowInsetsAnimationController {
+    method public void finish(boolean);
+    method @FloatRange(from=0.0f, to=1.0f) public float getCurrentFraction();
+    method @NonNull public android.graphics.Insets getCurrentInsets();
+    method @NonNull public android.graphics.Insets getHiddenStateInsets();
+    method @NonNull public android.graphics.Insets getShownStateInsets();
+    method public int getTypes();
+    method public void setInsetsAndAlpha(@Nullable android.graphics.Insets, @FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
+  }
+
+  public interface WindowInsetsController {
+    method public default void controlInputMethodAnimation(long, @NonNull android.view.WindowInsetsAnimationControlListener);
+    method public default void hideInputMethod();
+    method public void setSystemBarsAppearance(int);
+    method public void setSystemBarsBehavior(int);
+    method public default void showInputMethod();
+    field public static final int APPEARANCE_LIGHT_NAVIGATION_BARS = 16; // 0x10
+    field public static final int APPEARANCE_LIGHT_STATUS_BARS = 8; // 0x8
   }
 
   public interface WindowManager extends android.view.ViewManager {
diff --git a/api/removed.txt b/api/removed.txt
index 1a2f434..4e8e325 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -441,6 +441,9 @@
     method @Deprecated public static boolean getIncludePending(@NonNull android.net.Uri);
     method @Deprecated @NonNull public static android.provider.MediaStore.PendingSession openPending(@NonNull android.content.Context, @NonNull android.net.Uri);
     method @Deprecated @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri);
+    method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri);
+    method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long);
+    method @Deprecated public static void untrash(@NonNull android.content.Context, @NonNull android.net.Uri);
   }
 
   public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns {
diff --git a/api/system-current.txt b/api/system-current.txt
index e913665..8d55a33 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -546,10 +546,12 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method @Deprecated public void onGetInstantAppIntentFilter(@Nullable int[], @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback);
     method @Deprecated public void onGetInstantAppIntentFilter(@NonNull android.content.Intent, @Nullable int[], @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback);
-    method public void onGetInstantAppIntentFilter(@NonNull android.content.Intent, @Nullable int[], @NonNull android.os.UserHandle, @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback);
+    method @Deprecated public void onGetInstantAppIntentFilter(@NonNull android.content.Intent, @Nullable int[], @NonNull android.os.UserHandle, @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback);
+    method public void onGetInstantAppIntentFilter(@NonNull android.content.pm.InstantAppRequestInfo, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback);
     method @Deprecated public void onGetInstantAppResolveInfo(@Nullable int[], @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback);
     method @Deprecated public void onGetInstantAppResolveInfo(@NonNull android.content.Intent, @Nullable int[], @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback);
-    method public void onGetInstantAppResolveInfo(@NonNull android.content.Intent, @Nullable int[], @NonNull android.os.UserHandle, @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback);
+    method @Deprecated public void onGetInstantAppResolveInfo(@NonNull android.content.Intent, @Nullable int[], @NonNull android.os.UserHandle, @NonNull String, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback);
+    method public void onGetInstantAppResolveInfo(@NonNull android.content.pm.InstantAppRequestInfo, @NonNull android.app.InstantAppResolverService.InstantAppResolutionCallback);
   }
 
   public static final class InstantAppResolverService.InstantAppResolutionCallback {
@@ -809,6 +811,14 @@
 
 }
 
+package android.app.appsearch {
+
+  public class AppSearchManagerFrameworkInitializer {
+    method public static void initialize();
+  }
+
+}
+
 package android.app.assist {
 
   public static class AssistStructure.ViewNode {
@@ -1350,6 +1360,14 @@
     field public static final int OPTIONAL_CODECS_SUPPORT_UNKNOWN = -1; // 0xffffffff
   }
 
+  public final class BluetoothA2dpSink implements android.bluetooth.BluetoothProfile {
+    method public void finalize();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isAudioPlaying(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
+    field @RequiresPermission(android.Manifest.permission.BLUETOOTH) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
+  }
+
   public final class BluetoothAdapter {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean addOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connectAllEnabledProfiles(@NonNull android.bluetooth.BluetoothDevice);
@@ -1497,6 +1515,11 @@
     field public static final int REMOTE_PANU_ROLE = 2; // 0x2
   }
 
+  public class BluetoothPbap implements android.bluetooth.BluetoothProfile {
+    method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+    field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
+  }
+
   public interface BluetoothProfile {
     field public static final int CONNECTION_POLICY_ALLOWED = 100; // 0x64
     field public static final int CONNECTION_POLICY_FORBIDDEN = 0; // 0x0
@@ -1628,6 +1651,7 @@
     field public static final String STATS_MANAGER = "stats";
     field public static final String STATUS_BAR_SERVICE = "statusbar";
     field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
+    field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
     field public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry";
     field public static final String VR_SERVICE = "vrmanager";
     field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
@@ -1914,6 +1938,18 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.InstantAppIntentFilter> CREATOR;
   }
 
+  public final class InstantAppRequestInfo implements android.os.Parcelable {
+    ctor public InstantAppRequestInfo(@NonNull android.content.Intent, @Nullable int[], @NonNull android.os.UserHandle, boolean, @NonNull String);
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.InstantAppRequestInfo> CREATOR;
+    field @Nullable public final int[] hostDigestPrefix;
+    field @NonNull public final android.content.Intent intent;
+    field public final boolean isRequesterInstantApp;
+    field @NonNull public final String token;
+    field @NonNull public final android.os.UserHandle userHandle;
+  }
+
   public final class InstantAppResolveInfo implements android.os.Parcelable {
     ctor public InstantAppResolveInfo(@NonNull android.content.pm.InstantAppResolveInfo.InstantAppDigest, @Nullable String, @Nullable java.util.List<android.content.pm.InstantAppIntentFilter>, int);
     ctor public InstantAppResolveInfo(@NonNull android.content.pm.InstantAppResolveInfo.InstantAppDigest, @Nullable String, @Nullable java.util.List<android.content.pm.InstantAppIntentFilter>, long, @Nullable android.os.Bundle);
@@ -4167,18 +4203,20 @@
 package android.media.session {
 
   public final class MediaSessionManager {
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.Callback);
+    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void addOnMediaKeyEventDispatchedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.OnMediaKeyEventDispatchedListener);
+    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void addOnMediaKeyEventSessionChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.OnMediaKeyEventSessionChangedListener);
+    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void removeOnMediaKeyEventDispatchedListener(@NonNull android.media.session.MediaSessionManager.OnMediaKeyEventDispatchedListener);
+    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void removeOnMediaKeyEventSessionChangedListener(@NonNull android.media.session.MediaSessionManager.OnMediaKeyEventSessionChangedListener);
     method @RequiresPermission(android.Manifest.permission.SET_MEDIA_KEY_LISTENER) public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, @Nullable android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER) public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, @Nullable android.os.Handler);
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void unregisterCallback(@NonNull android.media.session.MediaSessionManager.Callback);
   }
 
-  public abstract static class MediaSessionManager.Callback {
-    ctor public MediaSessionManager.Callback();
-    method public abstract void onAddressedPlayerChanged(android.media.session.MediaSession.Token);
-    method public abstract void onAddressedPlayerChanged(android.content.ComponentName);
-    method public abstract void onMediaKeyEventDispatched(android.view.KeyEvent, android.media.session.MediaSession.Token);
-    method public abstract void onMediaKeyEventDispatched(android.view.KeyEvent, android.content.ComponentName);
+  public static interface MediaSessionManager.OnMediaKeyEventDispatchedListener {
+    method public default void onMediaKeyEventDispatched(@NonNull android.view.KeyEvent, @NonNull String, @NonNull android.media.session.MediaSession.Token);
+  }
+
+  public static interface MediaSessionManager.OnMediaKeyEventSessionChangedListener {
+    method public default void onMediaKeyEventSessionChanged(@NonNull String, @Nullable android.media.session.MediaSession.Token);
   }
 
   public static interface MediaSessionManager.OnMediaKeyListener {
@@ -4532,6 +4570,14 @@
     method public void onUpstreamChanged(@Nullable android.net.Network);
   }
 
+  public class InvalidPacketException extends java.lang.Exception {
+    ctor public InvalidPacketException(int);
+    field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
+    field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9
+    field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea
+    field public final int error;
+  }
+
   public final class IpConfiguration implements android.os.Parcelable {
     ctor public IpConfiguration();
     ctor public IpConfiguration(@NonNull android.net.IpConfiguration);
@@ -4582,6 +4628,15 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecTransform buildTunnelModeTransform(@NonNull java.net.InetAddress, @NonNull android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
   }
 
+  public class KeepalivePacketData {
+    ctor protected KeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[]) throws android.net.InvalidPacketException;
+    method @NonNull public byte[] getPacket();
+    field @NonNull public final java.net.InetAddress dstAddress;
+    field public final int dstPort;
+    field @NonNull public final java.net.InetAddress srcAddress;
+    field public final int srcPort;
+  }
+
   public class LinkAddress implements android.os.Parcelable {
     ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int);
     ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
@@ -4861,7 +4916,7 @@
     method @NonNull public java.util.List<android.net.LinkAddress> getInternalAddresses();
     method @NonNull public java.util.List<java.net.InetAddress> getInternalDhcpServers();
     method @NonNull public java.util.List<java.net.InetAddress> getInternalDnsServers();
-    method @NonNull public java.util.List<android.net.LinkAddress> getInternalSubnets();
+    method @NonNull public java.util.List<android.net.IpPrefix> getInternalSubnets();
     method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getOutboundTrafficSelectors();
   }
 
@@ -4930,6 +4985,7 @@
   public final class IkeSessionConfiguration {
     ctor public IkeSessionConfiguration();
     method @NonNull public String getRemoteApplicationVersion();
+    method @NonNull public java.util.List<byte[]> getRemoteVendorIDs();
     method public boolean isIkeExtensionEnabled(int);
     field public static final int EXTENSION_TYPE_FRAGMENTATION = 1; // 0x1
     field public static final int EXTENSION_TYPE_MOBIKE = 2; // 0x2
@@ -4949,9 +5005,9 @@
     ctor public IkeSessionParams.Builder();
     method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal);
     method @NonNull public android.net.ipsec.ike.IkeSessionParams build();
-    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey);
-    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.util.List<java.security.cert.X509Certificate>, @NonNull java.security.PrivateKey);
-    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthEap(@NonNull java.security.cert.X509Certificate, @NonNull android.net.eap.EapSessionConfig);
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@Nullable java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey);
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@Nullable java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.util.List<java.security.cert.X509Certificate>, @NonNull java.security.PrivateKey);
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthEap(@Nullable java.security.cert.X509Certificate, @NonNull android.net.eap.EapSessionConfig);
     method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthPsk(@NonNull byte[]);
     method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setLocalIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
     method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setRemoteIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
@@ -4969,7 +5025,7 @@
   }
 
   public static class IkeSessionParams.IkeAuthDigitalSignRemoteConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
-    method @NonNull public java.security.cert.X509Certificate getRemoteCaCert();
+    method @Nullable public java.security.cert.X509Certificate getRemoteCaCert();
   }
 
   public static class IkeSessionParams.IkeAuthEapConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
@@ -5033,12 +5089,10 @@
     ctor public TunnelModeChildSessionParams.Builder();
     method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
     method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(int);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(@NonNull java.net.InetAddress, int);
+    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(@NonNull java.net.Inet4Address);
+    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(@NonNull java.net.Inet6Address, int);
     method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDhcpServerRequest(int);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDhcpServerRequest(@NonNull java.net.InetAddress);
     method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDnsServerRequest(int);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDnsServerRequest(@NonNull java.net.InetAddress);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalSubnetRequest(int);
     method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
     method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
     method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams build();
@@ -5062,9 +5116,6 @@
   public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
   }
 
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Subnet extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
-  }
-
   public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
     method @Nullable public java.net.Inet6Address getAddress();
     method public int getPrefixLength();
@@ -5074,9 +5125,6 @@
     method @Nullable public java.net.Inet6Address getAddress();
   }
 
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Subnet extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
-  }
-
 }
 
 package android.net.ipsec.ike.exceptions {
@@ -7087,34 +7135,34 @@
     field public static final int STATUS_NOT_BLOCKED = 0; // 0x0
   }
 
-  public static final class ContactsContract.MetadataSync implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncColumns {
-    field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata";
-    field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata";
-    field public static final android.net.Uri CONTENT_URI;
-    field public static final String METADATA_AUTHORITY = "com.android.contacts.metadata";
-    field public static final android.net.Uri METADATA_AUTHORITY_URI;
+  @Deprecated public static final class ContactsContract.MetadataSync implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncColumns {
+    field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata";
+    field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata";
+    field @Deprecated public static final android.net.Uri CONTENT_URI;
+    field @Deprecated public static final String METADATA_AUTHORITY = "com.android.contacts.metadata";
+    field @Deprecated public static final android.net.Uri METADATA_AUTHORITY_URI;
   }
 
-  protected static interface ContactsContract.MetadataSyncColumns {
-    field public static final String ACCOUNT_NAME = "account_name";
-    field public static final String ACCOUNT_TYPE = "account_type";
-    field public static final String DATA = "data";
-    field public static final String DATA_SET = "data_set";
-    field public static final String DELETED = "deleted";
-    field public static final String RAW_CONTACT_BACKUP_ID = "raw_contact_backup_id";
+  @Deprecated protected static interface ContactsContract.MetadataSyncColumns {
+    field @Deprecated public static final String ACCOUNT_NAME = "account_name";
+    field @Deprecated public static final String ACCOUNT_TYPE = "account_type";
+    field @Deprecated public static final String DATA = "data";
+    field @Deprecated public static final String DATA_SET = "data_set";
+    field @Deprecated public static final String DELETED = "deleted";
+    field @Deprecated public static final String RAW_CONTACT_BACKUP_ID = "raw_contact_backup_id";
   }
 
-  public static final class ContactsContract.MetadataSyncState implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncStateColumns {
-    field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata_sync_state";
-    field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata_sync_state";
-    field public static final android.net.Uri CONTENT_URI;
+  @Deprecated public static final class ContactsContract.MetadataSyncState implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncStateColumns {
+    field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata_sync_state";
+    field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata_sync_state";
+    field @Deprecated public static final android.net.Uri CONTENT_URI;
   }
 
-  protected static interface ContactsContract.MetadataSyncStateColumns {
-    field public static final String ACCOUNT_NAME = "account_name";
-    field public static final String ACCOUNT_TYPE = "account_type";
-    field public static final String DATA_SET = "data_set";
-    field public static final String STATE = "state";
+  @Deprecated protected static interface ContactsContract.MetadataSyncStateColumns {
+    field @Deprecated public static final String ACCOUNT_NAME = "account_name";
+    field @Deprecated public static final String ACCOUNT_TYPE = "account_type";
+    field @Deprecated public static final String DATA_SET = "data_set";
+    field @Deprecated public static final String STATE = "state";
   }
 
   public final class DeviceConfig {
@@ -7322,6 +7370,7 @@
 
   public final class Settings {
     field public static final String ACTION_ACCESSIBILITY_DETAILS_SETTINGS = "android.settings.ACCESSIBILITY_DETAILS_SETTINGS";
+    field public static final String ACTION_BUGREPORT_HANDLER_SETTINGS = "android.settings.BUGREPORT_HANDLER_SETTINGS";
     field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
     field public static final String ACTION_LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS = "android.settings.LOCATION_CONTROLLER_EXTRA_PACKAGE_SETTINGS";
     field public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION = "android.settings.MANAGE_APP_OVERLAY_PERMISSION";
@@ -7998,6 +8047,7 @@
     field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions";
     field public static final String KEY_IMPORTANCE = "key_importance";
     field public static final String KEY_PEOPLE = "key_people";
+    field public static final String KEY_RANKING_SCORE = "key_ranking_score";
     field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     field public static final String KEY_TEXT_REPLIES = "key_text_replies";
     field public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
@@ -9280,7 +9330,6 @@
     method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
     method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
-    method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
     method public void onVoiceActivationStateChanged(int);
@@ -9290,7 +9339,6 @@
     field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
     field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
-    field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
     field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
     field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
@@ -9316,13 +9364,12 @@
   }
 
   public final class PreciseDataConnectionState implements android.os.Parcelable {
-    method public int describeContents();
-    method @Nullable public String getDataConnectionApn();
-    method public int getDataConnectionApnTypeBitMask();
-    method public int getDataConnectionFailCause();
-    method public int getDataConnectionState();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PreciseDataConnectionState> CREATOR;
+    method @Deprecated @NonNull public String getDataConnectionApn();
+    method @Deprecated public int getDataConnectionApnTypeBitMask();
+    method @Deprecated public int getDataConnectionFailCause();
+    method @Deprecated @Nullable public android.net.LinkProperties getDataConnectionLinkProperties();
+    method @Deprecated public int getDataConnectionNetworkType();
+    method @Deprecated public int getDataConnectionState();
   }
 
   public final class PreciseDisconnectCause {
@@ -9631,9 +9678,6 @@
 
   public class TelephonyManager {
     method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionReportDefaultNetworkStatus(int, boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionResetAll(int);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionSetRadioEnabled(int, boolean);
     method public int checkCarrierPrivilegesForPackage(String);
     method public int checkCarrierPrivilegesForPackageAnyPhone(String);
     method public void dial(String);
@@ -9707,9 +9751,11 @@
     method public boolean needsOtaServiceProvisioning();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
     method public void requestModemActivityInfo(@NonNull android.os.ResultReceiver);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
     method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
@@ -9723,6 +9769,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRadioEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadioPower(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerState(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerStateForSlot(int, int);
@@ -10333,6 +10380,11 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
   }
 
+  public class ImsManager {
+    method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
+    method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
+  }
+
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
     method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -10376,6 +10428,22 @@
     ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
   }
 
+  public class ImsRcsManager implements android.telephony.ims.RegistrationManager {
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerRcsAvailabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterRcsAvailabilityCallback(@NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
+  }
+
+  public static class ImsRcsManager.AvailabilityCallback {
+    ctor public ImsRcsManager.AvailabilityCallback();
+    method public void onAvailabilityChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
+  }
+
   public final class ImsReasonInfo implements android.os.Parcelable {
     field public static final String EXTRA_MSG_SERVICE_NOT_AUTHORIZED = "Forbidden. Not Authorized for Service";
   }
@@ -10737,9 +10805,22 @@
 
   public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
     ctor public RcsFeature();
-    method public void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+    method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+    method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
     method public void onFeatureReady();
     method public void onFeatureRemoved();
+    method public boolean queryCapabilityConfiguration(int, int);
+    method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
+  }
+
+  public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
+    ctor public RcsFeature.RcsImsCapabilities(int);
+    method public void addCapabilities(int);
+    method public boolean isCapable(int);
+    method public void removeCapabilities(int);
+    field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+    field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+    field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
   }
 
 }
diff --git a/api/test-current.txt b/api/test-current.txt
index 4942639..1c6bce0 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -711,6 +711,7 @@
     field public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
     field public static final String ROLLBACK_SERVICE = "rollback";
     field public static final String STATUS_BAR_SERVICE = "statusbar";
+    field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
     field public static final String TEST_NETWORK_SERVICE = "test_network";
   }
 
@@ -2831,6 +2832,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
     field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions";
     field public static final String KEY_IMPORTANCE = "key_importance";
+    field public static final String KEY_RANKING_SCORE = "key_ranking_score";
     field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     field public static final String KEY_TEXT_REPLIES = "key_text_replies";
     field public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
@@ -3161,6 +3163,10 @@
     field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
   }
 
+  public final class PreciseDataConnectionState implements android.os.Parcelable {
+    ctor @Deprecated public PreciseDataConnectionState(int, int, int, @NonNull String, @Nullable android.net.LinkProperties, int);
+  }
+
   public class ServiceState implements android.os.Parcelable {
     method public void addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo);
     method public void setCdmaSystemAndNetworkId(int, int);
@@ -3427,6 +3433,11 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
   }
 
+  public class ImsManager {
+    method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
+    method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
+  }
+
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
     method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -3470,6 +3481,22 @@
     ctor @Deprecated public ImsMmTelManager.RegistrationCallback();
   }
 
+  public class ImsRcsManager implements android.telephony.ims.RegistrationManager {
+    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isAvailable(int) throws android.telephony.ims.ImsException;
+    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isCapable(int, int) throws android.telephony.ims.ImsException;
+    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerRcsAvailabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
+    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterRcsAvailabilityCallback(@NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
+  }
+
+  public static class ImsRcsManager.AvailabilityCallback {
+    ctor public ImsRcsManager.AvailabilityCallback();
+    method public void onAvailabilityChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
+  }
+
   public class ImsService extends android.app.Service {
     ctor public ImsService();
     method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int);
@@ -3827,9 +3854,22 @@
 
   public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
     ctor public RcsFeature();
-    method public void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+    method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+    method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
     method public void onFeatureReady();
     method public void onFeatureRemoved();
+    method public boolean queryCapabilityConfiguration(int, int);
+    method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
+  }
+
+  public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
+    ctor public RcsFeature.RcsImsCapabilities(int);
+    method public void addCapabilities(int);
+    method public boolean isCapable(int);
+    method public void removeCapabilities(int);
+    field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+    field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+    field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
   }
 
 }
diff --git a/cmds/incident/main.cpp b/cmds/incident/main.cpp
index 6c3d197..eb2b98a 100644
--- a/cmds/incident/main.cpp
+++ b/cmds/incident/main.cpp
@@ -375,7 +375,7 @@
     if (destination == DEST_STDOUT) {
         // Call into the service
         sp<StatusListener> listener(new StatusListener());
-        status = service->reportIncidentToStream(args, listener, writeEnd);
+        status = service->reportIncidentToStream(args, listener, std::move(writeEnd));
 
         if (!status.isOk()) {
             fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
@@ -388,7 +388,7 @@
     } else if (destination == DEST_DUMPSTATE) {
         // Call into the service
         sp<StatusListener> listener(new StatusListener());
-        status = service->reportIncidentToDumpstate(writeEnd, listener);
+        status = service->reportIncidentToDumpstate(std::move(writeEnd), listener);
         if (!status.isOk()) {
             fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
             return 1;
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 999936b..cfd77c2 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -279,7 +279,7 @@
 
 Status IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
                                                const sp<IIncidentReportStatusListener>& listener,
-                                               const unique_fd& stream) {
+                                               unique_fd stream) {
     IncidentReportArgs argsCopy(args);
 
     // Streaming reports can not also be broadcast.
@@ -306,7 +306,7 @@
     return Status::ok();
 }
 
-Status IncidentService::reportIncidentToDumpstate(const unique_fd& stream,
+Status IncidentService::reportIncidentToDumpstate(unique_fd stream,
         const sp<IIncidentReportStatusListener>& listener) {
     uid_t caller = IPCThreadState::self()->getCallingUid();
     if (caller != AID_ROOT && caller != AID_SHELL) {
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index fb013d0..b2c7f23 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -121,9 +121,9 @@
 
     virtual Status reportIncidentToStream(const IncidentReportArgs& args,
                                           const sp<IIncidentReportStatusListener>& listener,
-                                          const unique_fd& stream);
+                                          unique_fd stream);
 
-    virtual Status reportIncidentToDumpstate(const unique_fd& stream,
+    virtual Status reportIncidentToDumpstate(unique_fd stream,
             const sp<IIncidentReportStatusListener>& listener);
 
     virtual Status systemRunning();
diff --git a/cmds/svc/src/com/android/commands/svc/DataCommand.java b/cmds/svc/src/com/android/commands/svc/DataCommand.java
index 35510cf..b4dbd1d 100644
--- a/cmds/svc/src/com/android/commands/svc/DataCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/DataCommand.java
@@ -16,12 +16,16 @@
 
 package com.android.commands.svc;
 
-import android.os.ServiceManager;
-import android.os.RemoteException;
-import android.content.Context;
-import com.android.internal.telephony.ITelephony;
-
+/**
+ * @deprecated Please use adb shell cmd phone data enabled/disable instead.
+ */
+@Deprecated
 public class DataCommand extends Svc.Command {
+
+    private static final String DECPRECATED_MESSAGE =
+            "adb shell svc data enable/disable is deprecated;"
+            + "please use adb shell cmd phone data enable/disable instead.";
+
     public DataCommand() {
         super("data");
     }
@@ -33,36 +37,10 @@
     public String longHelp() {
         return shortHelp() + "\n"
                 + "\n"
-                + "usage: svc data [enable|disable]\n"
-                + "         Turn mobile data on or off.\n\n";
+                + DECPRECATED_MESSAGE;
     }
 
     public void run(String[] args) {
-        boolean validCommand = false;
-        if (args.length >= 2) {
-            boolean flag = false;
-            if ("enable".equals(args[1])) {
-                flag = true;
-                validCommand = true;
-            } else if ("disable".equals(args[1])) {
-                flag = false;
-                validCommand = true;
-            }
-            if (validCommand) {
-                ITelephony phoneMgr
-                        = ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
-                try {
-                    if (flag) {
-                        phoneMgr.enableDataConnectivity();
-                    } else
-                        phoneMgr.disableDataConnectivity();
-                }
-                catch (RemoteException e) {
-                    System.err.println("Mobile data operation failed: " + e);
-                }
-                return;
-            }
-        }
-        System.err.println(longHelp());
+        System.err.println(DECPRECATED_MESSAGE);
     }
 }
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 8b62e2f..d82b151 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -17,6 +17,8 @@
 package android.accessibilityservice;
 
 
+import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD;
 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN;
 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT;
 import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT;
@@ -58,6 +60,8 @@
 
     /** @hide */
     @IntDef(prefix = { "GESTURE_" }, value = {
+            GESTURE_DOUBLE_TAP,
+            GESTURE_DOUBLE_TAP_AND_HOLD,
             GESTURE_SWIPE_UP,
             GESTURE_SWIPE_UP_AND_LEFT,
             GESTURE_SWIPE_UP_AND_DOWN,
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 47fdcde..0f619c8 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -300,6 +300,18 @@
     public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 16;
 
     /**
+     * The user has performed a double tap gesture on the touch screen.
+     * @hide
+     */
+    public static final int GESTURE_DOUBLE_TAP = 17;
+
+    /**
+     * The user has performed a double tap and hold gesture on the touch screen.
+     * @hide
+     */
+    public static final int GESTURE_DOUBLE_TAP_AND_HOLD = 18;
+
+    /**
      * The {@link Intent} that must be declared as handled by the service.
      */
     public static final String SERVICE_INTERFACE =
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index f4e465a..0f10c39 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -1943,35 +1943,6 @@
     }
 
     /**
-     * @hide
-     * Removes the shared account.
-     * @param account the account to remove
-     * @param user the user to remove the account from
-     * @return
-     */
-    public boolean removeSharedAccount(final Account account, UserHandle user) {
-        try {
-            boolean val = mService.removeSharedAccountAsUser(account, user.getIdentifier());
-            return val;
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * @hide
-     * @param user
-     * @return
-     */
-    public Account[] getSharedAccounts(UserHandle user) {
-        try {
-            return mService.getSharedAccountsAsUser(user.getIdentifier());
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Confirms that the user knows the password for an account to make extra
      * sure they are the owner of the account.  The user-entered password can
      * be supplied directly, otherwise the authenticator for this account type
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 4cf0a20..0127138 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -80,14 +80,11 @@
         String authTokenType);
 
     /* Shared accounts */
-    Account[] getSharedAccountsAsUser(int userId);
-    boolean removeSharedAccountAsUser(in Account account, int userId);
     void addSharedAccountsFromParentUser(int parentUserId, int userId, String opPackageName);
 
     /* Account renaming. */
     void renameAccount(in IAccountManagerResponse response, in Account accountToRename, String newName);
     String getPreviousName(in Account account);
-    boolean renameSharedAccountAsUser(in Account accountToRename, String newName, int userId);
 
     /* Add account in two steps. */
     void startAddAccountSession(in IAccountManagerResponse response, String accountType,
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 577272e..1e3b950 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -766,9 +766,19 @@
 
     private static final String REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:";
     private static final String AUTO_FILL_AUTH_WHO_PREFIX = "@android:autoFillAuth:";
-
     private static final String KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME = "com.android.systemui";
 
+    private static final int LOG_AM_ON_CREATE_CALLED = 30057;
+    private static final int LOG_AM_ON_START_CALLED = 30059;
+    private static final int LOG_AM_ON_RESUME_CALLED = 30022;
+    private static final int LOG_AM_ON_PAUSE_CALLED = 30021;
+    private static final int LOG_AM_ON_STOP_CALLED = 30049;
+    private static final int LOG_AM_ON_RESTART_CALLED = 30058;
+    private static final int LOG_AM_ON_DESTROY_CALLED = 30060;
+    private static final int LOG_AM_ON_ACTIVITY_RESULT_CALLED = 30062;
+    private static final int LOG_AM_ON_TOP_RESUMED_GAINED_CALLED = 30064;
+    private static final int LOG_AM_ON_TOP_RESUMED_LOST_CALLED = 30065;
+
     private static class ManagedDialog {
         Dialog mDialog;
         Bundle mArgs;
@@ -2439,8 +2449,11 @@
      * {@link #onProvideKeyboardShortcuts} to retrieve the shortcuts for the foreground activity.
      */
     public final void requestShowKeyboardShortcuts() {
+        final ComponentName sysuiComponent = ComponentName.unflattenFromString(
+                getResources().getString(
+                        com.android.internal.R.string.config_systemUIServiceComponent));
         Intent intent = new Intent(Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS);
-        intent.setPackage(KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME);
+        intent.setPackage(sysuiComponent.getPackageName());
         sendBroadcastAsUser(intent, Process.myUserHandle());
     }
 
@@ -2448,8 +2461,11 @@
      * Dismiss the Keyboard Shortcuts screen.
      */
     public final void dismissKeyboardShortcutsHelper() {
+        final ComponentName sysuiComponent = ComponentName.unflattenFromString(
+                getResources().getString(
+                        com.android.internal.R.string.config_systemUIServiceComponent));
         Intent intent = new Intent(Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS);
-        intent.setPackage(KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME);
+        intent.setPackage(sysuiComponent.getPackageName());
         sendBroadcastAsUser(intent, Process.myUserHandle());
     }
 
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 590b3db..795f51a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4417,4 +4417,18 @@
             }
         }
     }
+
+    /**
+     * Get packages of bugreport-whitelisted apps to handle a bug report.
+     *
+     * @return packages of bugreport-whitelisted apps to handle a bug report.
+     * @hide
+     */
+    public List<String> getBugreportWhitelistedPackages() {
+        try {
+            return getService().getBugreportWhitelistedPackages();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index aa8a302..112bd30 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -388,6 +388,7 @@
     void requestFullBugReport();
     void requestRemoteBugReport();
     boolean launchBugReportHandlerApp();
+    List<String> getBugreportWhitelistedPackages();
 
     @UnsupportedAppUsage
     Intent getIntentForIntentSender(in IIntentSender sender);
diff --git a/core/java/android/app/IInstantAppResolver.aidl b/core/java/android/app/IInstantAppResolver.aidl
index 7318762..8618fbb 100644
--- a/core/java/android/app/IInstantAppResolver.aidl
+++ b/core/java/android/app/IInstantAppResolver.aidl
@@ -16,15 +16,13 @@
 
 package android.app;
 
-import android.content.Intent;
+import android.content.pm.InstantAppRequestInfo;
 import android.os.IRemoteCallback;
 
 /** @hide */
 oneway interface IInstantAppResolver {
-    void getInstantAppResolveInfoList(in Intent sanitizedIntent, in int[] hostDigestPrefix,
-            int userId, String token, int sequence, IRemoteCallback callback);
+    void getInstantAppResolveInfoList(in InstantAppRequestInfo request, int sequence,
+            IRemoteCallback callback);
 
-    void getInstantAppIntentFilterList(in Intent sanitizedIntent, in int[] hostDigestPrefix,
-            int userId, String token, IRemoteCallback callback);
-
+    void getInstantAppIntentFilterList(in InstantAppRequestInfo request, IRemoteCallback callback);
 }
diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java
index a7be542..a413c60 100644
--- a/core/java/android/app/InstantAppResolverService.java
+++ b/core/java/android/app/InstantAppResolverService.java
@@ -21,6 +21,7 @@
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.InstantAppRequestInfo;
 import android.content.pm.InstantAppResolveInfo;
 import android.os.Build;
 import android.os.Bundle;
@@ -59,8 +60,9 @@
      * Called to retrieve resolve info for instant applications immediately.
      *
      * @param digestPrefix The hash prefix of the instant app's domain.
-     * @deprecated Should implement {@link #onGetInstantAppResolveInfo(Intent, int[], UserHandle,
-     *             String, InstantAppResolutionCallback)}.
+     *
+     * @deprecated Should implement {@link #onGetInstantAppResolveInfo(InstantAppRequestInfo,
+     *             InstantAppResolutionCallback)}
      */
     @Deprecated
     public void onGetInstantAppResolveInfo(@Nullable int[] digestPrefix, @NonNull String token,
@@ -73,8 +75,9 @@
      * sources.
      *
      * @param digestPrefix The hash prefix of the instant app's domain.
-     * @deprecated Should implement {@link #onGetInstantAppIntentFilter(Intent, int[], UserHandle,
-     *             String, InstantAppResolutionCallback)}.
+     *
+     * @deprecated Should implement {@link #onGetInstantAppIntentFilter(InstantAppRequestInfo,
+     *             InstantAppResolutionCallback)}
      */
     @Deprecated
     public void onGetInstantAppIntentFilter(@Nullable int[] digestPrefix, @NonNull String token,
@@ -103,8 +106,8 @@
      *
      * @see InstantAppResolveInfo
      *
-     * @deprecated Should implement {@link #onGetInstantAppResolveInfo(Intent, int[], UserHandle,
-     *             String, InstantAppResolutionCallback)}.
+     * @deprecated Should implement {@link #onGetInstantAppResolveInfo(InstantAppRequestInfo,
+     *             InstantAppResolutionCallback)}
      */
     @Deprecated
     public void onGetInstantAppResolveInfo(@NonNull Intent sanitizedIntent,
@@ -134,8 +137,8 @@
      *              {@link Intent#EXTRA_INSTANT_APP_TOKEN}.
      * @param callback The {@link InstantAppResolutionCallback} to provide results to.
      *
-     * @deprecated Should implement {@link #onGetInstantAppIntentFilter(Intent, int[], UserHandle,
-     *             String, InstantAppResolutionCallback)}.
+     * @deprecated Should implement {@link #onGetInstantAppIntentFilter(InstantAppRequestInfo,
+     *             InstantAppResolutionCallback)}
      */
     @Deprecated
     public void onGetInstantAppIntentFilter(@NonNull Intent sanitizedIntent,
@@ -170,7 +173,11 @@
      * @param callback The {@link InstantAppResolutionCallback} to provide results to.
      *
      * @see InstantAppResolveInfo
+     *
+     * @deprecated Should implement {@link #onGetInstantAppResolveInfo(InstantAppRequestInfo,
+     *             InstantAppResolutionCallback
      */
+    @Deprecated
     public void onGetInstantAppResolveInfo(@NonNull Intent sanitizedIntent,
             @Nullable int[] hostDigestPrefix, @NonNull UserHandle userHandle,
             @NonNull String token, @NonNull InstantAppResolutionCallback callback) {
@@ -193,7 +200,11 @@
      *              Intent, int[], UserHandle, String, InstantAppResolutionCallback)} and provided
      *              to the currently visible installer via {@link Intent#EXTRA_INSTANT_APP_TOKEN}.
      * @param callback The {@link InstantAppResolutionCallback} to provide results to.
+     *
+     * @deprecated Should implement {@link #onGetInstantAppIntentFilter(InstantAppRequestInfo,
+     *             InstantAppResolutionCallback)}
      */
+    @Deprecated
     public void onGetInstantAppIntentFilter(@NonNull Intent sanitizedIntent,
             @Nullable int[] hostDigestPrefix, @NonNull UserHandle userHandle,
             @NonNull String token, @NonNull InstantAppResolutionCallback callback) {
@@ -202,6 +213,41 @@
     }
 
     /**
+     * Called to retrieve resolve info for instant applications immediately. The response will be
+     * ignored if not provided within a reasonable time. {@link InstantAppResolveInfo}s provided
+     * in response to this method may be partial to request a second phase of resolution which will
+     * result in a subsequent call to {@link #onGetInstantAppIntentFilter(InstantAppRequestInfo,
+     * InstantAppResolutionCallback)}
+     *
+     * @param request The parameters for this resolution request
+     * @param callback The {@link InstantAppResolutionCallback} to provide results to.
+     *
+     * @see InstantAppResolveInfo
+     */
+    public void onGetInstantAppResolveInfo(@NonNull InstantAppRequestInfo request,
+            @NonNull InstantAppResolutionCallback callback) {
+        // If not overridden, forward to the old method.
+        onGetInstantAppResolveInfo(request.intent, request.hostDigestPrefix, request.userHandle,
+                request.token, callback);
+    }
+
+    /**
+     * Called to retrieve intent filters for potentially matching instant applications. Unlike
+     * {@link #onGetInstantAppResolveInfo(InstantAppRequestInfo, InstantAppResolutionCallback)},
+     * the response may take as long as necessary to respond. All {@link InstantAppResolveInfo}s
+     * provided in response to this method must be completely populated.
+     *
+     * @param request The parameters for this resolution request
+     * @param callback The {@link InstantAppResolutionCallback} to provide results to.
+     */
+    public void onGetInstantAppIntentFilter(@NonNull InstantAppRequestInfo request,
+            @NonNull InstantAppResolutionCallback callback) {
+        // If not overridden, forward to the old method.
+        onGetInstantAppIntentFilter(request.intent, request.hostDigestPrefix, request.userHandle,
+                request.token, callback);
+    }
+
+    /**
      * Returns a {@link Looper} to perform service operations on.
      */
     Looper getLooper() {
@@ -218,35 +264,29 @@
     public final IBinder onBind(Intent intent) {
         return new IInstantAppResolver.Stub() {
             @Override
-            public void getInstantAppResolveInfoList(Intent sanitizedIntent, int[] digestPrefix,
-                    int userId, String token, int sequence, IRemoteCallback callback) {
+            public void getInstantAppResolveInfoList(InstantAppRequestInfo request, int sequence,
+                    IRemoteCallback callback) {
                 if (DEBUG_INSTANT) {
-                    Slog.v(TAG, "[" + token + "] Phase1 called; posting");
+                    Slog.v(TAG, "[" + request.token + "] Phase1 called; posting");
                 }
                 final SomeArgs args = SomeArgs.obtain();
-                args.arg1 = callback;
-                args.arg2 = digestPrefix;
-                args.arg3 = userId;
-                args.arg4 = token;
-                args.arg5 = sanitizedIntent;
-                mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO,
-                        sequence, 0, args).sendToTarget();
+                args.arg1 = request;
+                args.arg2 = callback;
+                mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_RESOLVE_INFO, sequence,
+                        0, args).sendToTarget();
             }
 
             @Override
-            public void getInstantAppIntentFilterList(Intent sanitizedIntent,
-                    int[] digestPrefix, int userId, String token, IRemoteCallback callback) {
+            public void getInstantAppIntentFilterList(InstantAppRequestInfo request,
+                    IRemoteCallback callback) {
                 if (DEBUG_INSTANT) {
-                    Slog.v(TAG, "[" + token + "] Phase2 called; posting");
+                    Slog.v(TAG, "[" + request.token + "] Phase2 called; posting");
                 }
                 final SomeArgs args = SomeArgs.obtain();
-                args.arg1 = callback;
-                args.arg2 = digestPrefix;
-                args.arg3 = userId;
-                args.arg4 = token;
-                args.arg5 = sanitizedIntent;
-                mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER,
-                        args).sendToTarget();
+                args.arg1 = request;
+                args.arg2 = callback;
+                mHandler.obtainMessage(ServiceHandler.MSG_GET_INSTANT_APP_INTENT_FILTER, args)
+                        .sendToTarget();
             }
         };
     }
@@ -287,36 +327,33 @@
             switch (action) {
                 case MSG_GET_INSTANT_APP_RESOLVE_INFO: {
                     final SomeArgs args = (SomeArgs) message.obj;
-                    final IRemoteCallback callback = (IRemoteCallback) args.arg1;
-                    final int[] digestPrefix = (int[]) args.arg2;
-                    final int userId = (int) args.arg3;
-                    final String token = (String) args.arg4;
-                    final Intent intent = (Intent) args.arg5;
+                    final InstantAppRequestInfo request = (InstantAppRequestInfo) args.arg1;
+                    final IRemoteCallback callback = (IRemoteCallback) args.arg2;
+                    args.recycle();
                     final int sequence = message.arg1;
                     if (DEBUG_INSTANT) {
-                        Slog.d(TAG, "[" + token + "] Phase1 request;"
-                                + " prefix: " + Arrays.toString(digestPrefix)
-                                + ", userId: " + userId);
+                        Slog.d(TAG, "[" + request.token + "] Phase1 request;"
+                                + " prefix: " + Arrays.toString(request.hostDigestPrefix)
+                                + ", userId: " + request.userHandle.getIdentifier());
                     }
-                    onGetInstantAppResolveInfo(intent, digestPrefix, UserHandle.of(userId), token,
+                    onGetInstantAppResolveInfo(request,
                             new InstantAppResolutionCallback(sequence, callback));
                 } break;
 
                 case MSG_GET_INSTANT_APP_INTENT_FILTER: {
                     final SomeArgs args = (SomeArgs) message.obj;
-                    final IRemoteCallback callback = (IRemoteCallback) args.arg1;
-                    final int[] digestPrefix = (int[]) args.arg2;
-                    final int userId = (int) args.arg3;
-                    final String token = (String) args.arg4;
-                    final Intent intent = (Intent) args.arg5;
+                    final InstantAppRequestInfo request = (InstantAppRequestInfo) args.arg1;
+                    final IRemoteCallback callback = (IRemoteCallback) args.arg2;
+                    args.recycle();
                     if (DEBUG_INSTANT) {
-                        Slog.d(TAG, "[" + token + "] Phase2 request;"
-                                + " prefix: " + Arrays.toString(digestPrefix)
-                                + ", userId: " + userId);
+                        Slog.d(TAG, "[" + request.token + "] Phase2 request;"
+                                + " prefix: " + Arrays.toString(request.hostDigestPrefix)
+                                + ", userId: " + request.userHandle.getIdentifier());
                     }
-                    onGetInstantAppIntentFilter(intent, digestPrefix, UserHandle.of(userId), token,
+                    onGetInstantAppIntentFilter(request,
                             new InstantAppResolutionCallback(-1 /*sequence*/, callback));
-                } break;
+                }
+                break;
 
                 default: {
                     throw new IllegalArgumentException("Unknown message: " + action);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index bd948ec5..a1305da 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1145,6 +1145,14 @@
                         return new TimeZoneDetector();
                     }});
 
+        registerService(Context.TELEPHONY_IMS_SERVICE, android.telephony.ims.ImsManager.class,
+                new CachedServiceFetcher<android.telephony.ims.ImsManager>() {
+                    @Override
+                    public android.telephony.ims.ImsManager createService(ContextImpl ctx) {
+                        return new android.telephony.ims.ImsManager(ctx.getOuterContext());
+                    }
+                });
+
         registerService(Context.PERMISSION_SERVICE, PermissionManager.class,
                 new CachedServiceFetcher<PermissionManager>() {
                     @Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6e7ead1..7332978 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1349,6 +1349,9 @@
      * Broadcast action: send when any policy admin changes a policy.
      * This is generally used to find out when a new policy is in effect.
      *
+     * If the profile owner of an organization-owned managed profile changes some user
+     * restriction explicitly on the parent user, this broadcast will <em>not</em> be
+     * sent to the parent user.
      * @hide
      */
     @UnsupportedAppUsage
@@ -7958,18 +7961,23 @@
      * <p>
      * The calling device admin must be a profile or device owner; if it is not, a security
      * exception will be thrown.
+     * <p>
+     * The profile owner of an organization-owned managed profile may invoke this method on
+     * the {@link DevicePolicyManager} instance it obtained from
+     * {@link #getParentProfileInstance(ComponentName)}, for enforcing device-wide restrictions.
+     * <p>
+     * See the constants in {@link android.os.UserManager} for the list of restrictions that can
+     * be enforced device-wide.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param key The key of the restriction. See the constants in {@link android.os.UserManager}
-     *            for the list of keys.
+     * @param key   The key of the restriction.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void addUserRestriction(@NonNull ComponentName admin,
             @UserManager.UserRestrictionKey String key) {
-        throwIfParentInstance("addUserRestriction");
         if (mService != null) {
             try {
-                mService.setUserRestriction(admin, key, true);
+                mService.setUserRestriction(admin, key, true, mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -7981,18 +7989,22 @@
      * <p>
      * The calling device admin must be a profile or device owner; if it is not, a security
      * exception will be thrown.
+     * <p>
+     * The profile owner of an organization-owned managed profile may invoke this method on
+     * the {@link DevicePolicyManager} instance it obtained from
+     * {@link #getParentProfileInstance(ComponentName)}, for clearing device-wide restrictions.
+     * <p>
+     * See the constants in {@link android.os.UserManager} for the list of restrictions.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param key The key of the restriction. See the constants in {@link android.os.UserManager}
-     *            for the list of keys.
+     * @param key   The key of the restriction.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void clearUserRestriction(@NonNull ComponentName admin,
             @UserManager.UserRestrictionKey String key) {
-        throwIfParentInstance("clearUserRestriction");
         if (mService != null) {
             try {
-                mService.setUserRestriction(admin, key, false);
+                mService.setUserRestriction(admin, key, false, mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -8006,16 +8018,20 @@
      * The target user may have more restrictions set by the system or other device owner / profile
      * owner. To get all the user restrictions currently set, use
      * {@link UserManager#getUserRestrictions()}.
+     * <p>
+     * The profile owner of an organization-owned managed profile may invoke this method on
+     * the {@link DevicePolicyManager} instance it obtained from
+     * {@link #getParentProfileInstance(ComponentName)}, for retrieving device-wide restrictions
+     * it previously set with {@link #addUserRestriction(ComponentName, String)}.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public @NonNull Bundle getUserRestrictions(@NonNull ComponentName admin) {
-        throwIfParentInstance("getUserRestrictions");
         Bundle ret = null;
         if (mService != null) {
             try {
-                ret = mService.getUserRestrictions(admin);
+                ret = mService.getUserRestrictions(admin, mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 949e8ab..9c82ff6 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -208,8 +208,8 @@
     void setRestrictionsProvider(in ComponentName who, in ComponentName provider);
     ComponentName getRestrictionsProvider(int userHandle);
 
-    void setUserRestriction(in ComponentName who, in String key, boolean enable);
-    Bundle getUserRestrictions(in ComponentName who);
+    void setUserRestriction(in ComponentName who, in String key, boolean enable, boolean parent);
+    Bundle getUserRestrictions(in ComponentName who, boolean parent);
     void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
     void clearCrossProfileIntentFilters(in ComponentName admin);
 
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 5e530ee..bd1eea5 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -515,8 +515,8 @@
     public static PendingIntent createPermissionIntent(Context context, Uri sliceUri,
             String callingPackage) {
         Intent intent = new Intent(SliceManager.ACTION_REQUEST_SLICE_PERMISSION);
-        intent.setComponent(new ComponentName("com.android.systemui",
-                "com.android.systemui.SlicePermissionActivity"));
+        intent.setComponent(ComponentName.unflattenFromString(context.getResources().getString(
+                com.android.internal.R.string.config_slicePermissionComponent)));
         intent.putExtra(EXTRA_BIND_URI, sliceUri);
         intent.putExtra(EXTRA_PKG, callingPackage);
         intent.putExtra(EXTRA_PROVIDER_PKG, context.getPackageName());
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index cf33676..c6957e1 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -17,7 +17,9 @@
 package android.bluetooth;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -39,6 +41,7 @@
  *
  * @hide
  */
+@SystemApi
 public final class BluetoothA2dpSink implements BluetoothProfile {
     private static final String TAG = "BluetoothA2dpSink";
     private static final boolean DBG = true;
@@ -59,71 +62,14 @@
      * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
      * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
-     * receive.
+     * @hide
      */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public static final String ACTION_CONNECTION_STATE_CHANGED =
             "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
 
-    /**
-     * Intent used to broadcast the change in the Playing state of the A2DP Sink
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
-     * receive.
-     */
-    public static final String ACTION_PLAYING_STATE_CHANGED =
-            "android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED";
-
-    /**
-     * A2DP sink device is streaming music. This state can be one of
-     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
-     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
-     */
-    public static final int STATE_PLAYING = 10;
-
-    /**
-     * A2DP sink device is NOT streaming music. This state can be one of
-     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
-     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
-     */
-    public static final int STATE_NOT_PLAYING = 11;
-
-    /**
-     * Intent used to broadcast the change in the Playing state of the A2DP Sink
-     * profile.
-     *
-     * <p>This intent will have 3 extras:
-     * <ul>
-     * <li> {@link #EXTRA_AUDIO_CONFIG} - The audio configuration for the remote device. </li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
-     * </ul>
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
-     * receive.
-     */
-    public static final String ACTION_AUDIO_CONFIG_CHANGED =
-            "android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED";
-
-    /**
-     * Extra for the {@link #ACTION_AUDIO_CONFIG_CHANGED} intent.
-     *
-     * This extra represents the current audio configuration of the A2DP source device.
-     * {@see BluetoothAudioConfig}
-     */
-    public static final String EXTRA_AUDIO_CONFIG =
-            "android.bluetooth.a2dp-sink.profile.extra.AUDIO_CONFIG";
-
     private BluetoothAdapter mAdapter;
     private final BluetoothProfileConnector<IBluetoothA2dpSink> mProfileConnector =
             new BluetoothProfileConnector(this, BluetoothProfile.A2DP_SINK,
@@ -170,13 +116,11 @@
      * the state. Users can get the connection state of the profile
      * from this intent.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
-     * permission.
-     *
      * @param device Remote Bluetooth Device
      * @return false on immediate error, true otherwise
      * @hide
      */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
         final IBluetoothA2dpSink service = getService();
@@ -210,14 +154,12 @@
      * {@link #STATE_DISCONNECTING} can be used to distinguish between the
      * two scenarios.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
-     * permission.
-     *
      * @param device Remote Bluetooth Device
      * @return false on immediate error, true otherwise
      * @hide
      */
     @UnsupportedAppUsage
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean disconnect(BluetoothDevice device) {
         if (DBG) log("disconnect(" + device + ")");
         final IBluetoothA2dpSink service = getService();
@@ -235,6 +177,8 @@
 
     /**
      * {@inheritDoc}
+     *
+     * @hide
      */
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
@@ -254,6 +198,8 @@
 
     /**
      * {@inheritDoc}
+     *
+     * @hide
      */
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
@@ -273,6 +219,8 @@
 
     /**
      * {@inheritDoc}
+     *
+     * @hide
      */
     @Override
     public int getConnectionState(BluetoothDevice device) {
@@ -300,6 +248,8 @@
      * @return audio configuration for the device, or null
      *
      * {@see BluetoothAudioConfig}
+     *
+     * @hide
      */
     public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
         if (VDBG) log("getAudioConfig(" + device + ")");
@@ -347,7 +297,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+    public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
         final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
@@ -395,7 +345,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public int getConnectionPolicy(BluetoothDevice device) {
+    public int getConnectionPolicy(@Nullable BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
@@ -411,13 +361,16 @@
     }
 
     /**
-     * Check if A2DP profile is streaming music.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     * Check if audio is playing on the bluetooth device (A2DP profile is streaming music).
      *
      * @param device BluetoothDevice device
+     * @return true if audio is playing (A2dp is streaming music), false otherwise
+     *
+     * @hide
      */
-    public boolean isA2dpPlaying(BluetoothDevice device) {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public boolean isAudioPlaying(@Nullable BluetoothDevice device) {
         final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
@@ -448,9 +401,9 @@
                 return "connected";
             case STATE_DISCONNECTING:
                 return "disconnecting";
-            case STATE_PLAYING:
+            case BluetoothA2dp.STATE_PLAYING:
                 return "playing";
-            case STATE_NOT_PLAYING:
+            case BluetoothA2dp.STATE_NOT_PLAYING:
                 return "not playing";
             default:
                 return "<unknown state " + state + ">";
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index d94c657..df02896 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -16,7 +16,10 @@
 
 package android.bluetooth;
 
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -54,6 +57,7 @@
  *
  * @hide
  */
+@SystemApi
 public class BluetoothPbap implements BluetoothProfile {
 
     private static final String TAG = "BluetoothPbap";
@@ -75,7 +79,11 @@
      *  {@link BluetoothProfile#STATE_DISCONNECTING}.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
      * receive.
+     *
+     * @hide
      */
+    @SuppressLint("ActionValue")
+    @SystemApi
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_CONNECTION_STATE_CHANGED =
             "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
@@ -85,33 +93,16 @@
     private ServiceListener mServiceListener;
     private BluetoothAdapter mAdapter;
 
+    /** @hide */
     public static final int RESULT_FAILURE = 0;
+    /** @hide */
     public static final int RESULT_SUCCESS = 1;
-    /** Connection canceled before completion. */
-    public static final int RESULT_CANCELED = 2;
-
     /**
-     * An interface for notifying Bluetooth PCE IPC clients when they have
-     * been connected to the BluetoothPbap service.
+     * Connection canceled before completion.
+     *
+     * @hide
      */
-    public interface ServiceListener {
-        /**
-         * Called to notify the client when this proxy object has been
-         * connected to the BluetoothPbap service. Clients must wait for
-         * this callback before making IPC calls on the BluetoothPbap
-         * service.
-         */
-        public void onServiceConnected(BluetoothPbap proxy);
-
-        /**
-         * Called to notify the client that this proxy object has been
-         * disconnected from the BluetoothPbap service. Clients must not
-         * make IPC calls on the BluetoothPbap service after this callback.
-         * This callback will currently only occur if the application hosting
-         * the BluetoothPbap service, but may be called more often in future.
-         */
-        public void onServiceDisconnected();
-    }
+    public static final int RESULT_CANCELED = 2;
 
     private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
             new IBluetoothStateChangeCallback.Stub() {
@@ -127,6 +118,8 @@
 
     /**
      * Create a BluetoothPbap proxy object.
+     *
+     * @hide
      */
     public BluetoothPbap(Context context, ServiceListener l) {
         mContext = context;
@@ -181,6 +174,7 @@
         }
     }
 
+    /** @hide */
     protected void finalize() throws Throwable {
         try {
             close();
@@ -194,6 +188,8 @@
      * Other public functions of BluetoothPbap will return default error
      * results once close() has been called. Multiple invocations of close()
      * are ok.
+     *
+     * @hide
      */
     public synchronized void close() {
         IBluetoothManager mgr = mAdapter.getBluetoothManager();
@@ -210,6 +206,8 @@
 
     /**
      * {@inheritDoc}
+     *
+     * @hide
      */
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
@@ -229,17 +227,22 @@
 
     /**
      * {@inheritDoc}
+     *
+     * @hide
      */
+    @SystemApi
     @Override
-    public int getConnectionState(BluetoothDevice device) {
+    public int getConnectionState(@Nullable BluetoothDevice device) {
         log("getConnectionState: device=" + device);
-        final IBluetoothPbap service = mService;
-        if (service == null) {
-            Log.w(TAG, "Proxy not attached to service");
-            return BluetoothProfile.STATE_DISCONNECTED;
-        }
         try {
-            return service.getConnectionState(device);
+            final IBluetoothPbap service = mService;
+            if (service != null && isEnabled() && isValidDevice(device)) {
+                return service.getConnectionState(device);
+            }
+            if (service == null) {
+                Log.w(TAG, "Proxy not attached to service");
+            }
+            return BluetoothProfile.STATE_DISCONNECTED;
         } catch (RemoteException e) {
             Log.e(TAG, e.toString());
         }
@@ -248,6 +251,8 @@
 
     /**
      * {@inheritDoc}
+     *
+     * @hide
      */
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
@@ -266,22 +271,12 @@
     }
 
     /**
-     * Returns true if the specified Bluetooth device is connected (does not
-     * include connecting). Returns false if not connected, or if this proxy
-     * object is not currently connected to the Pbap service.
-     */
-    // TODO: This is currently being used by SettingsLib and internal app.
-    public boolean isConnected(BluetoothDevice device) {
-        return getConnectionState(device) == BluetoothAdapter.STATE_CONNECTED;
-    }
-
-    /**
      * Disconnects the current Pbap client (PCE). Currently this call blocks,
      * it may soon be made asynchronous. Returns false if this proxy object is
      * not currently connected to the Pbap service.
+     *
+     * @hide
      */
-    // TODO: This is currently being used by SettingsLib and will be used in the future.
-    // TODO: Must specify target device. Implement this in the service.
     @UnsupportedAppUsage
     public boolean disconnect(BluetoothDevice device) {
         log("disconnect()");
@@ -304,7 +299,7 @@
             log("Proxy object connected");
             mService = IBluetoothPbap.Stub.asInterface(service);
             if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothPbap.this);
+                mServiceListener.onServiceConnected(BluetoothProfile.PBAP, BluetoothPbap.this);
             }
         }
 
@@ -312,11 +307,23 @@
             log("Proxy object disconnected");
             doUnbind();
             if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected();
+                mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP);
             }
         }
     };
 
+    private boolean isEnabled() {
+        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+        return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+        if (device == null) return false;
+
+        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+        return false;
+    }
+
     private static void log(String msg) {
         if (DBG) {
             Log.d(TAG, msg);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 85027d9..d084449 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4336,6 +4336,15 @@
     public static final String SOUND_TRIGGER_SERVICE = "soundtrigger";
 
     /**
+     * Use with {@link #getSystemService(String)} to access the
+     * {@link com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareService}.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    public static final String SOUND_TRIGGER_MIDDLEWARE_SERVICE = "soundtrigger_middleware";
+
+    /**
      * Official published name of the (internal) permission service.
      *
      * @see #getSystemService(String)
@@ -4916,6 +4925,8 @@
      * {@link android.telephony.ims.ImsManager}.
      * @hide
      */
+    @SystemApi
+    @TestApi
     public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
 
     /**
diff --git a/core/java/android/content/pm/AuxiliaryResolveInfo.java b/core/java/android/content/pm/AuxiliaryResolveInfo.java
index 202df50..7d07e1d 100644
--- a/core/java/android/content/pm/AuxiliaryResolveInfo.java
+++ b/core/java/android/content/pm/AuxiliaryResolveInfo.java
@@ -46,17 +46,21 @@
     public final Intent failureIntent;
     /** The matching filters for this resolve info. */
     public final List<AuxiliaryFilter> filters;
+    /** Stored {@link InstantAppRequest#hostDigestPrefixSecure} to prevent re-generation */
+    public final int[] hostDigestPrefixSecure;
 
     /** Create a response for installing an instant application. */
     public AuxiliaryResolveInfo(@NonNull String token,
             boolean needsPhase2,
             @Nullable Intent failureIntent,
-            @Nullable List<AuxiliaryFilter> filters) {
+            @Nullable List<AuxiliaryFilter> filters,
+            @Nullable int[] hostDigestPrefix) {
         this.token = token;
         this.needsPhaseTwo = needsPhase2;
         this.failureIntent = failureIntent;
         this.filters = filters;
         this.installFailureActivity = null;
+        this.hostDigestPrefixSecure = hostDigestPrefix;
     }
 
     /** Create a response for installing a split on demand. */
@@ -69,6 +73,7 @@
         this.token = null;
         this.needsPhaseTwo = false;
         this.failureIntent = failureIntent;
+        this.hostDigestPrefixSecure = null;
     }
 
     /** Create a response for installing a split on demand. */
@@ -126,4 +131,4 @@
                     + ", splitName='" + splitName + '\'' + '}';
         }
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/pm/BaseParceledListSlice.java b/core/java/android/content/pm/BaseParceledListSlice.java
index 4178309..ffbca16 100644
--- a/core/java/android/content/pm/BaseParceledListSlice.java
+++ b/core/java/android/content/pm/BaseParceledListSlice.java
@@ -47,7 +47,7 @@
      * TODO get this number from somewhere else. For now set it to a quarter of
      * the 1MB limit.
      */
-    private static final int MAX_IPC_SIZE = IBinder.MAX_IPC_SIZE;
+    private static final int MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes();
 
     private final List<T> mList;
 
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 0b3c765..e954635 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -39,6 +39,9 @@
     void transfer(in String packageName, in IntentSender statusReceiver);
     void abandon();
 
+    void addFile(String name, long lengthBytes, in byte[] metadata);
+    void removeFile(String name);
+
     boolean isMultiPackage();
     int[] getChildSessionIds();
     void addChildSessionId(in int sessionId);
@@ -46,5 +49,4 @@
     int getParentSessionId();
 
     boolean isStaged();
-    void addFile(in String name, long size, in byte[] metadata);
 }
diff --git a/core/java/android/content/pm/InstantAppRequest.java b/core/java/android/content/pm/InstantAppRequest.java
index 361d4e4..f692db1 100644
--- a/core/java/android/content/pm/InstantAppRequest.java
+++ b/core/java/android/content/pm/InstantAppRequest.java
@@ -16,9 +16,10 @@
 
 package android.content.pm;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Intent;
 import android.os.Bundle;
-import android.text.TextUtils;
 
 /**
  * Information needed to make an instant application resolution request.
@@ -34,32 +35,40 @@
     public final String resolvedType;
     /** The name of the package requesting the instant application */
     public final String callingPackage;
+    /** Whether or not the requesting package was an instant app */
+    public final boolean isRequesterInstantApp;
     /** ID of the user requesting the instant application */
     public final int userId;
     /**
      * Optional extra bundle provided by the source application to the installer for additional
-     * verification. */
+     * verification.
+     */
     public final Bundle verificationBundle;
     /** Whether resolution occurs because an application is starting */
     public final boolean resolveForStart;
-    /** The instant app digest for this request */
-    public final InstantAppResolveInfo.InstantAppDigest digest;
+    /**
+     * The hash prefix of an instant app's domain or null if no host is defined.
+     * Secure version that should be carried through for external use.
+     */
+    @Nullable
+    public final int[] hostDigestPrefixSecure;
+    /** A unique identifier */
+    @NonNull
+    public final String token;
 
     public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent,
-            String resolvedType, String callingPackage, int userId, Bundle verificationBundle,
-            boolean resolveForStart) {
+            String resolvedType, String callingPackage, boolean isRequesterInstantApp,
+            int userId, Bundle verificationBundle, boolean resolveForStart,
+            @Nullable int[] hostDigestPrefixSecure, @NonNull String token) {
         this.responseObj = responseObj;
         this.origIntent = origIntent;
         this.resolvedType = resolvedType;
         this.callingPackage = callingPackage;
+        this.isRequesterInstantApp = isRequesterInstantApp;
         this.userId = userId;
         this.verificationBundle = verificationBundle;
         this.resolveForStart = resolveForStart;
-        if (origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost())) {
-            digest = new InstantAppResolveInfo.InstantAppDigest(
-                    origIntent.getData().getHost(), 5 /*maxDigests*/);
-        } else {
-            digest = InstantAppResolveInfo.InstantAppDigest.UNDEFINED;
-        }
+        this.hostDigestPrefixSecure = hostDigestPrefixSecure;
+        this.token = token;
     }
 }
diff --git a/core/java/android/content/pm/InstantAppRequestInfo.aidl b/core/java/android/content/pm/InstantAppRequestInfo.aidl
new file mode 100644
index 0000000..0f94220
--- /dev/null
+++ b/core/java/android/content/pm/InstantAppRequestInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.content.pm;
+
+parcelable InstantAppRequestInfo;
diff --git a/core/java/android/content/pm/InstantAppRequestInfo.java b/core/java/android/content/pm/InstantAppRequestInfo.java
new file mode 100644
index 0000000..83d5536
--- /dev/null
+++ b/core/java/android/content/pm/InstantAppRequestInfo.java
@@ -0,0 +1,188 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Intent;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Information exposed to {@link android.app.InstantAppResolverService} to complete
+ * an instant application resolution request.
+ * @hide
+ */
+@SystemApi
+@DataClass(genParcelable = true, genConstructor = true, genAidl = true)
+public final class InstantAppRequestInfo implements Parcelable {
+
+    /**
+     * The sanitized {@link Intent} used for resolution. A sanitized Intent is an intent with
+     * potential PII removed from the original intent. Fields removed include extras and the
+     * host + path of the data, if defined.
+     */
+    @NonNull
+    public final Intent intent;
+
+    /** The hash prefix of the instant app's domain or null if no host is defined. */
+    @Nullable
+    public final int[] hostDigestPrefix;
+
+    /** The user requesting the instant application */
+    @NonNull
+    public final UserHandle userHandle;
+
+    /** Whether or not the requesting package was an instant app itself */
+    public final boolean isRequesterInstantApp;
+
+    /** A unique identifier */
+    @NonNull
+    public final String token;
+
+
+
+    // Code below generated by codegen v1.0.13.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/InstantAppRequestInfo.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new InstantAppRequestInfo.
+     *
+     * @param intent
+     *   The sanitized {@link Intent} used for resolution. A sanitized Intent is an intent with
+     *   potential PII removed from the original intent. Fields removed include extras and the
+     *   host + path of the data, if defined.
+     * @param hostDigestPrefix
+     *   The hash prefix of the instant app's domain or null if no host is defined.
+     * @param userHandle
+     *   The user requesting the instant application
+     * @param isRequesterInstantApp
+     *   Whether or not the requesting package was an instant app itself
+     * @param token
+     *   A unique identifier
+     */
+    @DataClass.Generated.Member
+    public InstantAppRequestInfo(
+            @NonNull Intent intent,
+            @Nullable int[] hostDigestPrefix,
+            @NonNull UserHandle userHandle,
+            boolean isRequesterInstantApp,
+            @NonNull String token) {
+        this.intent = intent;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, intent);
+        this.hostDigestPrefix = hostDigestPrefix;
+        this.userHandle = userHandle;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, userHandle);
+        this.isRequesterInstantApp = isRequesterInstantApp;
+        this.token = token;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, token);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (isRequesterInstantApp) flg |= 0x8;
+        if (hostDigestPrefix != null) flg |= 0x2;
+        dest.writeByte(flg);
+        dest.writeTypedObject(intent, flags);
+        if (hostDigestPrefix != null) dest.writeIntArray(hostDigestPrefix);
+        dest.writeTypedObject(userHandle, flags);
+        dest.writeString(token);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ InstantAppRequestInfo(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        boolean _isRequesterInstantApp = (flg & 0x8) != 0;
+        Intent _intent = (Intent) in.readTypedObject(Intent.CREATOR);
+        int[] _hostDigestPrefix = (flg & 0x2) == 0 ? null : in.createIntArray();
+        UserHandle _userHandle = (UserHandle) in.readTypedObject(UserHandle.CREATOR);
+        String _token = in.readString();
+
+        this.intent = _intent;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, intent);
+        this.hostDigestPrefix = _hostDigestPrefix;
+        this.userHandle = _userHandle;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, userHandle);
+        this.isRequesterInstantApp = _isRequesterInstantApp;
+        this.token = _token;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, token);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<InstantAppRequestInfo> CREATOR
+            = new Parcelable.Creator<InstantAppRequestInfo>() {
+        @Override
+        public InstantAppRequestInfo[] newArray(int size) {
+            return new InstantAppRequestInfo[size];
+        }
+
+        @Override
+        public InstantAppRequestInfo createFromParcel(@NonNull android.os.Parcel in) {
+            return new InstantAppRequestInfo(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1574373347443L,
+            codegenVersion = "1.0.13",
+            sourceFile = "frameworks/base/core/java/android/content/pm/InstantAppRequestInfo.java",
+            inputSignatures = "public final @android.annotation.NonNull android.content.Intent intent\npublic final @android.annotation.Nullable int[] hostDigestPrefix\npublic final @android.annotation.NonNull android.os.UserHandle userHandle\npublic final  boolean isRequesterInstantApp\npublic final @android.annotation.NonNull java.lang.String token\nclass InstantAppRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=true, genAidl=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 8f51435..898631e 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -231,6 +231,15 @@
     public static final String EXTRA_CALLBACK = "android.content.pm.extra.CALLBACK";
 
     /**
+     * Streaming installation pending.
+     * Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
+     *
+     * @see #EXTRA_SESSION_ID
+     * {@hide}
+     */
+    public static final int STATUS_PENDING_STREAMING = -2;
+
+    /**
      * User action is currently required to proceed. You can launch the intent
      * activity described by {@link Intent#EXTRA_INTENT} to involve the user and
      * continue.
@@ -1059,13 +1068,56 @@
             }
         }
 
+
+        /**
+         * Adds a file to session. On commit this file will be pulled from dataLoader.
+         *
+         * @param name arbitrary, unique name of your choosing to identify the
+         *            APK being written. You can open a file again for
+         *            additional writes (such as after a reboot) by using the
+         *            same name. This name is only meaningful within the context
+         *            of a single install session.
+         * @param lengthBytes total size of the file being written.
+         *            The system may clear various caches as needed to allocate
+         *            this space.
+         * @param metadata additional info use by dataLoader to pull data for the file.
+         * @throws SecurityException if called after the session has been
+         *             sealed or abandoned
+         * @throws IllegalStateException if called for non-callback session
+         * {@hide}
+         */
+        public void addFile(@NonNull String name, long lengthBytes, @NonNull byte[] metadata) {
+            try {
+                mSession.addFile(name, lengthBytes, metadata);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Removes a file.
+         *
+         * @param name name of a file, e.g. split.
+         * @throws SecurityException if called after the session has been
+         *             sealed or abandoned
+         * @throws IllegalStateException if called for non-callback session
+         * {@hide}
+         */
+        public void removeFile(@NonNull String name) {
+            try {
+                mSession.removeFile(name);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
         /**
          * Attempt to commit everything staged in this session. This may require
          * user intervention, and so it may not happen immediately. The final
          * result of the commit will be reported through the given callback.
          * <p>
-         * Once this method is called, the session is sealed and no additional
-         * mutations may be performed on the session. If the device reboots
+         * Once this method is called, the session is sealed and no additional mutations may be
+         * performed on the session. In case of device reboot or data loader transient failure
          * before the session has been finalized, you may commit the session again.
          * <p>
          * If the installer is the device owner or the affiliated profile owner, there will be no
@@ -1220,27 +1272,6 @@
         }
 
         /**
-         * Configure files for an installation session.
-         *
-         * Currently only for Incremental installation session. Once this method is called,
-         * the files and their paths, as specified in the parameters, will be created and properly
-         * configured in the Incremental File System.
-         *
-         * TODO(b/136132412): update this and InstallationFile class with latest API design.
-         *
-         * @throws IllegalStateException if {@link SessionParams#incrementalParams} is null.
-         *
-         * @hide
-         */
-        public void addFile(@NonNull String name, long size, @NonNull byte[] metadata) {
-            try {
-                mSession.addFile(name, size, metadata);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        /**
          * Release this session object. You can open the session again if it
          * hasn't been finalized.
          */
@@ -1429,6 +1460,9 @@
         public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
         /** {@hide} */
         public IncrementalDataLoaderParams incrementalParams;
+        /** TODO(b/146080380): add a class name to make it fully compatible with ComponentName.
+         * {@hide} */
+        public String dataLoaderPackageName;
 
         /**
          * Construct parameters for a new package install session.
@@ -1468,6 +1502,7 @@
                 incrementalParams = new IncrementalDataLoaderParams(
                         dataLoaderParamsParcel);
             }
+            dataLoaderPackageName = source.readString();
         }
 
         /** {@hide} */
@@ -1492,6 +1527,7 @@
             ret.isStaged = isStaged;
             ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
             ret.incrementalParams = incrementalParams;
+            ret.dataLoaderPackageName = dataLoaderPackageName;
             return ret;
         }
 
@@ -1831,6 +1867,20 @@
             this.incrementalParams = incrementalParams;
         }
 
+        /**
+         * Set the data provider params for the session.
+         * This also switches installation into callback mode and disallow direct writes into
+         * staging folder.
+         * TODO(b/146080380): unify dataprovider params with Incremental.
+         *
+         * @param dataLoaderPackageName name of the dataLoader package
+         * {@hide}
+         */
+        @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
+        public void setDataLoaderPackageName(String dataLoaderPackageName) {
+            this.dataLoaderPackageName = dataLoaderPackageName;
+        }
+
         /** {@hide} */
         public void dump(IndentingPrintWriter pw) {
             pw.printPair("mode", mode);
@@ -1851,6 +1901,7 @@
             pw.printPair("isMultiPackage", isMultiPackage);
             pw.printPair("isStaged", isStaged);
             pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode);
+            pw.printPair("dataLoaderPackageName", dataLoaderPackageName);
             pw.println();
         }
 
@@ -1885,6 +1936,7 @@
             } else {
                 dest.writeParcelable(null, flags);
             }
+            dest.writeString(dataLoaderPackageName);
         }
 
         public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java
index 515185e..35df474 100644
--- a/core/java/android/content/pm/parsing/AndroidPackage.java
+++ b/core/java/android/content/pm/parsing/AndroidPackage.java
@@ -229,6 +229,11 @@
 
     String getOverlayTargetName();
 
+    /**
+     * Map of overlayable name to actor name.
+     */
+    Map<String, String> getOverlayables();
+
     // TODO(b/135203078): Does this and getAppInfoPackageName have to be separate methods?
     //  The refactor makes them the same value with no known consequences, so should be redundant.
     String getPackageName();
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index 0deb2ab..7732316 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -52,6 +52,7 @@
 import android.content.pm.split.DefaultSplitAssetLoader;
 import android.content.pm.split.SplitAssetDependencyLoader;
 import android.content.pm.split.SplitAssetLoader;
+import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -92,6 +93,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /** @hide */
@@ -287,8 +289,23 @@
                                 + result.getErrorMessage());
             }
 
-            return result.getResultAndNull()
-                    .setVolumeUuid(volumeUuid)
+            ParsingPackage pkg = result.getResultAndNull();
+            ApkAssets apkAssets = assets.getApkAssets()[0];
+            if (apkAssets.definesOverlayable()) {
+                SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
+                int size = packageNames.size();
+                for (int index = 0; index < size; index++) {
+                    String packageName = packageNames.get(index);
+                    Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
+                    if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
+                        for (String overlayable : overlayableToActor.keySet()) {
+                            pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
+                        }
+                    }
+                }
+            }
+
+            return pkg.setVolumeUuid(volumeUuid)
                     .setApplicationVolumeUuid(volumeUuid)
                     .setSigningDetails(SigningDetails.UNKNOWN);
         } catch (PackageParserException e) {
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
index 377279e..0e736d5 100644
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -18,6 +18,8 @@
 
 import static android.os.Build.VERSION_CODES.DONUT;
 
+import static java.util.Collections.emptyMap;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Intent;
@@ -55,11 +57,13 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
 import com.android.server.SystemConfig;
 
 import java.security.PublicKey;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -126,6 +130,7 @@
     private String overlayCategory;
     private int overlayPriority;
     private boolean overlayIsStatic;
+    private Map<String, String> overlayables = emptyMap();
 
     private String staticSharedLibName;
     private long staticSharedLibVersion;
@@ -475,7 +480,7 @@
 
     @Override
     public Map<String, ArraySet<PublicKey>> getKeySetMapping() {
-        return keySetMapping == null ? Collections.emptyMap() : keySetMapping;
+        return keySetMapping == null ? emptyMap() : keySetMapping;
     }
 
     @Override
@@ -773,6 +778,13 @@
     }
 
     @Override
+    public ParsingPackage addOverlayable(String overlayableName, String actorName) {
+        this.overlayables = CollectionUtils.add(this.overlayables,
+                TextUtils.safeIntern(overlayableName), TextUtils.safeIntern(actorName));
+        return this;
+    }
+
+    @Override
     public PackageImpl addAdoptPermission(String adoptPermission) {
         this.adoptPermissions = ArrayUtils.add(this.adoptPermissions, adoptPermission);
         return this;
@@ -2125,6 +2137,11 @@
     }
 
     @Override
+    public Map<String, String> getOverlayables() {
+        return overlayables;
+    }
+
+    @Override
     public boolean isOverlayIsStatic() {
         return overlayIsStatic;
     }
@@ -2291,7 +2308,7 @@
         appInfo.metaData = appMetaData;
         appInfo.minAspectRatio = minAspectRatio;
         appInfo.minSdkVersion = minSdkVersion;
-        appInfo.name = name;
+        appInfo.name = className;
         if (appInfo.name != null) {
             appInfo.name = appInfo.name.trim();
         }
@@ -2957,6 +2974,7 @@
         dest.writeString(this.overlayCategory);
         dest.writeInt(this.overlayPriority);
         dest.writeBoolean(this.overlayIsStatic);
+        dest.writeMap(this.overlayables);
         dest.writeString(this.staticSharedLibName);
         dest.writeLong(this.staticSharedLibVersion);
         dest.writeStringList(this.libraryNames);
@@ -3100,6 +3118,8 @@
         this.overlayCategory = in.readString();
         this.overlayPriority = in.readInt();
         this.overlayIsStatic = in.readBoolean();
+        this.overlayables = new HashMap<>();
+        in.readMap(overlayables, boot);
         this.staticSharedLibName = TextUtils.safeIntern(in.readString());
         this.staticSharedLibVersion = in.readLong();
         this.libraryNames = in.createStringArrayList();
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 43c1f6e..aff1b2e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -62,6 +62,8 @@
 
     ParsingPackage addOriginalPackage(String originalPackage);
 
+    ParsingPackage addOverlayable(String overlayableName, String actorName);
+
     ParsingPackage addPermission(ParsedPermission permission);
 
     ParsingPackage addPermissionGroup(ParsedPermissionGroup permissionGroup);
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 1f29d1a..c84c4a7 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -935,7 +935,7 @@
     /**
      * <p>List of the maximum number of regions that can be used for metering in
      * auto-exposure (AE), auto-white balance (AWB), and auto-focus (AF);
-     * this corresponds to the the maximum number of elements in
+     * this corresponds to the maximum number of elements in
      * {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}, {@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions},
      * and {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}.</p>
      * <p><b>Range of valid values:</b><br></p>
@@ -955,7 +955,7 @@
     /**
      * <p>The maximum number of metering regions that can be used by the auto-exposure (AE)
      * routine.</p>
-     * <p>This corresponds to the the maximum allowed number of elements in
+     * <p>This corresponds to the maximum allowed number of elements in
      * {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}.</p>
      * <p><b>Range of valid values:</b><br>
      * Value will be &gt;= 0. For FULL-capability devices, this
@@ -973,7 +973,7 @@
     /**
      * <p>The maximum number of metering regions that can be used by the auto-white balance (AWB)
      * routine.</p>
-     * <p>This corresponds to the the maximum allowed number of elements in
+     * <p>This corresponds to the maximum allowed number of elements in
      * {@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions}.</p>
      * <p><b>Range of valid values:</b><br>
      * Value will be &gt;= 0.</p>
@@ -989,7 +989,7 @@
 
     /**
      * <p>The maximum number of metering regions that can be used by the auto-focus (AF) routine.</p>
-     * <p>This corresponds to the the maximum allowed number of elements in
+     * <p>This corresponds to the maximum allowed number of elements in
      * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}.</p>
      * <p><b>Range of valid values:</b><br>
      * Value will be &gt;= 0. For FULL-capability devices, this
@@ -1987,6 +1987,7 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA SECURE_IMAGE_DATA}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA SYSTEM_CAMERA}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING OFFLINE_PROCESSING}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -2006,6 +2007,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME
      * @see #REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA
      * @see #REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING
      */
     @PublicKey
     @NonNull
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 799c716..f2a7abd 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1004,6 +1004,51 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14;
 
+    /**
+     * <p>The camera device supports the OFFLINE_PROCESSING use case.</p>
+     * <p>With OFFLINE_PROCESSING capability, the application can switch an ongoing
+     * capture session to offline mode by calling the
+     * CameraCaptureSession#switchToOffline method and specify streams to be kept in offline
+     * mode. The camera will then stop currently active repeating requests, prepare for
+     * some requests to go into offline mode, and return an offline session object. After
+     * the switchToOffline call returns, the original capture session is in closed state as
+     * if the CameraCaptureSession#close method has been called.
+     * In the offline mode, all inflight requests will continue to be processed in the
+     * background, and the application can immediately close the camera or create a new
+     * capture session without losing those requests' output images and capture results.</p>
+     * <p>While the camera device is processing offline requests, it
+     * might not be able to support all stream configurations it can support
+     * without offline requests. When that happens, the createCaptureSession
+     * method call will fail. The following stream configurations are guaranteed to work
+     * without hitting the resource busy exception:</p>
+     * <ul>
+     * <li>One ongoing offline session: target one output surface of YUV or
+     * JPEG format, any resolution.</li>
+     * <li>The active camera capture session:<ol>
+     * <li>One preview surface (SurfaceView or SurfaceTexture) up to 1920 width</li>
+     * <li>One YUV ImageReader surface up to 1920 width</li>
+     * <li>One Jpeg ImageReader, any resolution: the camera device is
+     *    allowed to slow down JPEG output speed by 50% if there is any ongoing offline
+     *    session.</li>
+     * <li>One ImageWriter surface of private format, any resolution if the device supports
+     *    PRIVATE_REPROCESSING capability</li>
+     * </ol>
+     * </li>
+     * <li>Alternatively, the active camera session above can be replaced by an legacy
+     * {@link android.hardware.Camera Camera} with the following parameter settings:<ol>
+     * <li>Preview size up to 1920 width</li>
+     * <li>Preview callback size up to 1920 width</li>
+     * <li>Video size up to 1920 width</li>
+     * <li>Picture size, any resolution: the camera device is
+     *     allowed to slow down JPEG output speed by 50% if there is any ongoing offline
+     *     session.</li>
+     * </ol>
+     * </li>
+     * </ul>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_OFFLINE_PROCESSING = 15;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index bcbc337..41435c9 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -29,6 +29,7 @@
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
+import android.hardware.camera2.ICameraOfflineSession;
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.OutputConfiguration;
@@ -866,6 +867,38 @@
         }
     }
 
+    public void switchToOffline(ICameraDeviceCallbacks cbs, Surface[] offlineOutputs)
+            throws CameraAccessException {
+        if ((offlineOutputs == null) || (offlineOutputs.length == 0)) {
+            throw new IllegalArgumentException("Invalid offline outputs!");
+        }
+        if (cbs == null) {
+            throw new IllegalArgumentException("Invalid device callbacks!");
+        }
+
+        ICameraOfflineSession offlineSession = null;
+        synchronized(mInterfaceLock) {
+            int streamId = -1;
+            for (Surface surface : offlineOutputs) {
+                for (int i = 0; i < mConfiguredOutputs.size(); i++) {
+                    if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
+                        streamId = mConfiguredOutputs.keyAt(i);
+                        break;
+                    }
+                }
+                if (streamId == -1) {
+                    throw new IllegalArgumentException("Offline surface is not part of this" +
+                            " session");
+                }
+            }
+
+            offlineSession = mRemoteDevice.switchToOffline(cbs,
+                    offlineOutputs);
+            // TODO: Initialize CameraOfflineSession wrapper, clear 'mConfiguredOutputs',
+            // and update request tracking
+        }
+    }
+
     public void tearDown(Surface surface) throws CameraAccessException {
         if (surface == null) throw new IllegalArgumentException("Surface is null");
 
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index 3660f29..397417b 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -28,6 +28,8 @@
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.ICameraDeviceUser;
+import android.hardware.camera2.ICameraDeviceCallbacks;
+import android.hardware.camera2.ICameraOfflineSession;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.params.SessionConfiguration;
@@ -248,6 +250,17 @@
         }
     }
 
+    public ICameraOfflineSession switchToOffline(ICameraDeviceCallbacks cbs,
+            Surface[] offlineOutputs)
+            throws CameraAccessException {
+        try {
+            return mRemoteDevice.switchToOffline(cbs, offlineOutputs);
+        } catch (Throwable t) {
+            CameraManager.throwAsPublicException(t);
+            throw new UnsupportedOperationException("Unexpected exception", t);
+        }
+    }
+
     public void finalizeOutputConfigurations(int streamId, OutputConfiguration deferredConfig)
             throws CameraAccessException {
         try {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 5d1435a..6ab0c29 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -24,6 +24,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
+import android.hardware.camera2.ICameraOfflineSession;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.CaptureResultExtras;
 import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
@@ -789,6 +790,12 @@
     }
 
     @Override
+    public ICameraOfflineSession switchToOffline(ICameraDeviceCallbacks cbs,
+            Surface[] offlineOutputs) {
+        throw new UnsupportedOperationException("Legacy device does not support switchToOffline");
+    }
+
+    @Override
     public IBinder asBinder() {
         // This is solely intended to be used for in-process binding.
         return null;
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 6eaf54b..23f18a8 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -1163,7 +1163,7 @@
             if (orderedPreviewSizes != null) {
                 for (Size size : orderedPreviewSizes) {
                     if ((mDisplaySize.getWidth() >= size.getWidth()) &&
-                            (mDisplaySize.getWidth() >= size.getHeight())) {
+                            (mDisplaySize.getHeight() >= size.getHeight())) {
                         return size;
                     }
                 }
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
new file mode 100644
index 0000000..8231c58
--- /dev/null
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.soundtrigger;
+
+import android.hardware.soundtrigger.ModelParams;
+import android.media.AudioFormat;
+import android.media.audio.common.AudioConfig;
+import android.media.soundtrigger_middleware.ConfidenceLevel;
+import android.media.soundtrigger_middleware.ModelParameterRange;
+import android.media.soundtrigger_middleware.Phrase;
+import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
+import android.media.soundtrigger_middleware.PhraseSoundModel;
+import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger_middleware.RecognitionMode;
+import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
+
+import android.annotation.Nullable;
+
+import java.util.Arrays;
+import java.util.UUID;
+
+/** @hide */
+class ConversionUtil {
+    public static SoundTrigger.ModuleProperties aidl2apiModuleDescriptor(
+            SoundTriggerModuleDescriptor aidlDesc) {
+        SoundTriggerModuleProperties properties = aidlDesc.properties;
+        return new SoundTrigger.ModuleProperties(
+                aidlDesc.handle,
+                properties.implementor,
+                properties.description,
+                properties.uuid,
+                properties.version,
+                properties.maxSoundModels,
+                properties.maxKeyPhrases,
+                properties.maxUsers,
+                aidl2apiRecognitionModes(properties.recognitionModes),
+                properties.captureTransition,
+                properties.maxBufferMs,
+                properties.concurrentCapture,
+                properties.powerConsumptionMw,
+                properties.triggerInEvent
+        );
+    }
+
+    public static int aidl2apiRecognitionModes(int aidlModes) {
+        int result = 0;
+        if ((aidlModes & RecognitionMode.VOICE_TRIGGER) != 0) {
+            result |= SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
+        }
+        if ((aidlModes & RecognitionMode.USER_IDENTIFICATION) != 0) {
+            result |= SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
+        }
+        if ((aidlModes & RecognitionMode.USER_AUTHENTICATION) != 0) {
+            result |= SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION;
+        }
+        if ((aidlModes & RecognitionMode.GENERIC_TRIGGER) != 0) {
+            result |= SoundTrigger.RECOGNITION_MODE_GENERIC;
+        }
+        return result;
+    }
+
+    public static int api2aidlRecognitionModes(int apiModes) {
+        int result = 0;
+        if ((apiModes & SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER) != 0) {
+            result |= RecognitionMode.VOICE_TRIGGER;
+        }
+        if ((apiModes & SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION) != 0) {
+            result |= RecognitionMode.USER_IDENTIFICATION;
+        }
+        if ((apiModes & SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION) != 0) {
+            result |= RecognitionMode.USER_AUTHENTICATION;
+        }
+        if ((apiModes & SoundTrigger.RECOGNITION_MODE_GENERIC) != 0) {
+            result |= RecognitionMode.GENERIC_TRIGGER;
+        }
+        return result;
+    }
+
+
+    public static SoundModel api2aidlGenericSoundModel(SoundTrigger.GenericSoundModel apiModel) {
+        return api2aidlSoundModel(apiModel);
+    }
+
+    public static SoundModel api2aidlSoundModel(SoundTrigger.SoundModel apiModel) {
+        SoundModel aidlModel = new SoundModel();
+        aidlModel.type = apiModel.type;
+        aidlModel.uuid = api2aidlUuid(apiModel.uuid);
+        aidlModel.vendorUuid = api2aidlUuid(apiModel.vendorUuid);
+        aidlModel.data = Arrays.copyOf(apiModel.data, apiModel.data.length);
+        return aidlModel;
+    }
+
+    public static String api2aidlUuid(UUID apiUuid) {
+        return apiUuid.toString();
+    }
+
+    public static PhraseSoundModel api2aidlPhraseSoundModel(
+            SoundTrigger.KeyphraseSoundModel apiModel) {
+        PhraseSoundModel aidlModel = new PhraseSoundModel();
+        aidlModel.common = api2aidlSoundModel(apiModel);
+        aidlModel.phrases = new Phrase[apiModel.keyphrases.length];
+        for (int i = 0; i < apiModel.keyphrases.length; ++i) {
+            aidlModel.phrases[i] = api2aidlPhrase(apiModel.keyphrases[i]);
+        }
+        return aidlModel;
+    }
+
+    public static Phrase api2aidlPhrase(SoundTrigger.Keyphrase apiPhrase) {
+        Phrase aidlPhrase = new Phrase();
+        aidlPhrase.id = apiPhrase.id;
+        aidlPhrase.recognitionModes = api2aidlRecognitionModes(apiPhrase.recognitionModes);
+        aidlPhrase.users = Arrays.copyOf(apiPhrase.users, apiPhrase.users.length);
+        aidlPhrase.locale = apiPhrase.locale;
+        aidlPhrase.text = apiPhrase.text;
+        return aidlPhrase;
+    }
+
+    public static RecognitionConfig api2aidlRecognitionConfig(
+            SoundTrigger.RecognitionConfig apiConfig) {
+        RecognitionConfig aidlConfig = new RecognitionConfig();
+        aidlConfig.captureRequested = apiConfig.captureRequested;
+        // apiConfig.allowMultipleTriggers is ignored by the lower layers.
+        aidlConfig.phraseRecognitionExtras =
+                new PhraseRecognitionExtra[apiConfig.keyphrases.length];
+        for (int i = 0; i < apiConfig.keyphrases.length; ++i) {
+            aidlConfig.phraseRecognitionExtras[i] = api2aidlPhraseRecognitionExtra(
+                    apiConfig.keyphrases[i]);
+        }
+        aidlConfig.data = Arrays.copyOf(apiConfig.data, apiConfig.data.length);
+        return aidlConfig;
+    }
+
+    public static PhraseRecognitionExtra api2aidlPhraseRecognitionExtra(
+            SoundTrigger.KeyphraseRecognitionExtra apiExtra) {
+        PhraseRecognitionExtra aidlExtra = new PhraseRecognitionExtra();
+        aidlExtra.id = apiExtra.id;
+        aidlExtra.recognitionModes = api2aidlRecognitionModes(apiExtra.recognitionModes);
+        aidlExtra.confidenceLevel = apiExtra.coarseConfidenceLevel;
+        aidlExtra.levels = new ConfidenceLevel[apiExtra.confidenceLevels.length];
+        for (int i = 0; i < apiExtra.confidenceLevels.length; ++i) {
+            aidlExtra.levels[i] = api2aidlConfidenceLevel(apiExtra.confidenceLevels[i]);
+        }
+        return aidlExtra;
+    }
+
+    public static SoundTrigger.KeyphraseRecognitionExtra aidl2apiPhraseRecognitionExtra(
+            PhraseRecognitionExtra aidlExtra) {
+        SoundTrigger.ConfidenceLevel[] apiLevels =
+                new SoundTrigger.ConfidenceLevel[aidlExtra.levels.length];
+        for (int i = 0; i < aidlExtra.levels.length; ++i) {
+            apiLevels[i] = aidl2apiConfidenceLevel(aidlExtra.levels[i]);
+        }
+        return new SoundTrigger.KeyphraseRecognitionExtra(aidlExtra.id,
+                aidl2apiRecognitionModes(aidlExtra.recognitionModes),
+                aidlExtra.confidenceLevel, apiLevels);
+    }
+
+    public static ConfidenceLevel api2aidlConfidenceLevel(
+            SoundTrigger.ConfidenceLevel apiLevel) {
+        ConfidenceLevel aidlLevel = new ConfidenceLevel();
+        aidlLevel.levelPercent = apiLevel.confidenceLevel;
+        aidlLevel.userId = apiLevel.userId;
+        return aidlLevel;
+    }
+
+    public static SoundTrigger.ConfidenceLevel aidl2apiConfidenceLevel(
+            ConfidenceLevel apiLevel) {
+        return new SoundTrigger.ConfidenceLevel(apiLevel.userId, apiLevel.levelPercent);
+    }
+
+    public static SoundTrigger.RecognitionEvent aidl2apiRecognitionEvent(
+            int modelHandle, RecognitionEvent aidlEvent) {
+        return new SoundTrigger.GenericRecognitionEvent(
+                aidlEvent.status,
+                modelHandle, aidlEvent.captureAvailable, aidlEvent.captureSession,
+                aidlEvent.captureDelayMs, aidlEvent.capturePreambleMs, aidlEvent.triggerInData,
+                aidl2apiAudioFormat(aidlEvent.audioConfig), aidlEvent.data);
+    }
+
+    public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent(
+            int modelHandle,
+            PhraseRecognitionEvent aidlEvent) {
+        SoundTrigger.KeyphraseRecognitionExtra[] apiExtras =
+                new SoundTrigger.KeyphraseRecognitionExtra[aidlEvent.phraseExtras.length];
+        for (int i = 0; i < aidlEvent.phraseExtras.length; ++i) {
+            apiExtras[i] = aidl2apiPhraseRecognitionExtra(aidlEvent.phraseExtras[i]);
+        }
+        return new SoundTrigger.KeyphraseRecognitionEvent(aidlEvent.common.status, modelHandle,
+                aidlEvent.common.captureAvailable,
+                aidlEvent.common.captureSession, aidlEvent.common.captureDelayMs,
+                aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData,
+                aidl2apiAudioFormat(aidlEvent.common.audioConfig), aidlEvent.common.data,
+                apiExtras);
+    }
+
+    public static AudioFormat aidl2apiAudioFormat(AudioConfig audioConfig) {
+        AudioFormat.Builder apiBuilder = new AudioFormat.Builder();
+        apiBuilder.setSampleRate(audioConfig.sampleRateHz);
+        apiBuilder.setChannelMask(aidl2apiChannelInMask(audioConfig.channelMask));
+        apiBuilder.setEncoding(aidl2apiEncoding(audioConfig.format));
+        return apiBuilder.build();
+    }
+
+    public static int aidl2apiEncoding(int aidlFormat) {
+        switch (aidlFormat) {
+            case android.media.audio.common.AudioFormat.PCM
+                    | android.media.audio.common.AudioFormat.PCM_SUB_16_BIT:
+                return AudioFormat.ENCODING_PCM_16BIT;
+
+            case android.media.audio.common.AudioFormat.PCM
+                    | android.media.audio.common.AudioFormat.PCM_SUB_8_BIT:
+                return AudioFormat.ENCODING_PCM_8BIT;
+
+            case android.media.audio.common.AudioFormat.PCM
+                    | android.media.audio.common.AudioFormat.PCM_SUB_FLOAT:
+            case android.media.audio.common.AudioFormat.PCM
+                    | android.media.audio.common.AudioFormat.PCM_SUB_8_24_BIT:
+            case android.media.audio.common.AudioFormat.PCM
+                    | android.media.audio.common.AudioFormat.PCM_SUB_24_BIT_PACKED:
+            case android.media.audio.common.AudioFormat.PCM
+                    | android.media.audio.common.AudioFormat.PCM_SUB_32_BIT:
+                return AudioFormat.ENCODING_PCM_FLOAT;
+
+            case android.media.audio.common.AudioFormat.AC3:
+                return AudioFormat.ENCODING_AC3;
+
+            case android.media.audio.common.AudioFormat.E_AC3:
+                return AudioFormat.ENCODING_E_AC3;
+
+            case android.media.audio.common.AudioFormat.DTS:
+                return AudioFormat.ENCODING_DTS;
+
+            case android.media.audio.common.AudioFormat.DTS_HD:
+                return AudioFormat.ENCODING_DTS_HD;
+
+            case android.media.audio.common.AudioFormat.MP3:
+                return AudioFormat.ENCODING_MP3;
+
+            case android.media.audio.common.AudioFormat.AAC
+                    | android.media.audio.common.AudioFormat.AAC_SUB_LC:
+                return AudioFormat.ENCODING_AAC_LC;
+
+            case android.media.audio.common.AudioFormat.AAC
+                    | android.media.audio.common.AudioFormat.AAC_SUB_HE_V1:
+                return AudioFormat.ENCODING_AAC_HE_V1;
+
+            case android.media.audio.common.AudioFormat.AAC
+                    | android.media.audio.common.AudioFormat.AAC_SUB_HE_V2:
+                return AudioFormat.ENCODING_AAC_HE_V2;
+
+            case android.media.audio.common.AudioFormat.IEC61937:
+                return AudioFormat.ENCODING_IEC61937;
+
+            case android.media.audio.common.AudioFormat.DOLBY_TRUEHD:
+                return AudioFormat.ENCODING_DOLBY_TRUEHD;
+
+            case android.media.audio.common.AudioFormat.AAC
+                    | android.media.audio.common.AudioFormat.AAC_SUB_ELD:
+                return AudioFormat.ENCODING_AAC_ELD;
+
+            case android.media.audio.common.AudioFormat.AAC
+                    | android.media.audio.common.AudioFormat.AAC_SUB_XHE:
+                return AudioFormat.ENCODING_AAC_XHE;
+
+            case android.media.audio.common.AudioFormat.AC4:
+                return AudioFormat.ENCODING_AC4;
+
+            case android.media.audio.common.AudioFormat.E_AC3
+                    | android.media.audio.common.AudioFormat.E_AC3_SUB_JOC:
+                return AudioFormat.ENCODING_E_AC3_JOC;
+
+            case android.media.audio.common.AudioFormat.MAT:
+            case android.media.audio.common.AudioFormat.MAT
+                    | android.media.audio.common.AudioFormat.MAT_SUB_1_0:
+            case android.media.audio.common.AudioFormat.MAT
+                    | android.media.audio.common.AudioFormat.MAT_SUB_2_0:
+            case android.media.audio.common.AudioFormat.MAT
+                    | android.media.audio.common.AudioFormat.MAT_SUB_2_1:
+                return AudioFormat.ENCODING_DOLBY_MAT;
+
+            case android.media.audio.common.AudioFormat.DEFAULT:
+                return AudioFormat.ENCODING_DEFAULT;
+
+            default:
+                return AudioFormat.ENCODING_INVALID;
+        }
+    }
+
+    public static int api2aidlModelParameter(int apiParam) {
+        switch (apiParam) {
+            case ModelParams.THRESHOLD_FACTOR:
+                return android.media.soundtrigger_middleware.ModelParameter.THRESHOLD_FACTOR;
+            default:
+                return android.media.soundtrigger_middleware.ModelParameter.INVALID;
+        }
+    }
+
+    public static int aidl2apiChannelInMask(int aidlMask) {
+        // We're assuming AudioFormat.CHANNEL_IN_* constants are kept in sync with
+        // android.media.audio.common.AudioChannelMask.
+        return aidlMask;
+    }
+
+    public static SoundTrigger.ModelParamRange aidl2apiModelParameterRange(
+            @Nullable ModelParameterRange aidlRange) {
+        if (aidlRange == null) {
+            return null;
+        }
+        return new SoundTrigger.ModelParamRange(aidlRange.minInclusive, aidlRange.maxInclusive);
+    }
+}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 86f3eec..5484df4 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -22,18 +22,29 @@
 import static android.system.OsConstants.EPERM;
 import static android.system.OsConstants.EPIPE;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
+import android.content.Context;
 import android.media.AudioFormat;
+import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
+import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
 import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.UUID;
 
 /**
@@ -44,6 +55,7 @@
  */
 @SystemApi
 public class SoundTrigger {
+    private static final String TAG = "SoundTrigger";
 
     private SoundTrigger() {
     }
@@ -119,15 +131,15 @@
          * recognition callback event */
         public final boolean returnsTriggerInEvent;
 
-        ModuleProperties(int id, String implementor, String description,
-                String uuid, int version, int maxSoundModels, int maxKeyphrases,
+        ModuleProperties(int id, @NonNull String implementor, @NonNull String description,
+                @NonNull String uuid, int version, int maxSoundModels, int maxKeyphrases,
                 int maxUsers, int recognitionModes, boolean supportsCaptureTransition,
                 int maxBufferMs, boolean supportsConcurrentCapture,
                 int powerConsumptionMw, boolean returnsTriggerInEvent) {
             this.id = id;
-            this.implementor = implementor;
-            this.description = description;
-            this.uuid = UUID.fromString(uuid);
+            this.implementor = requireNonNull(implementor);
+            this.description = requireNonNull(description);
+            this.uuid = UUID.fromString(requireNonNull(uuid));
             this.version = version;
             this.maxSoundModels = maxSoundModels;
             this.maxKeyphrases = maxKeyphrases;
@@ -231,6 +243,7 @@
 
         /** Unique sound model identifier */
         @UnsupportedAppUsage
+        @NonNull
         public final UUID uuid;
 
         /** Sound model type (e.g. TYPE_KEYPHRASE); */
@@ -238,17 +251,20 @@
 
         /** Unique sound model vendor identifier */
         @UnsupportedAppUsage
+        @NonNull
         public final UUID vendorUuid;
 
         /** Opaque data. For use by vendor implementation and enrollment application */
         @UnsupportedAppUsage
+        @NonNull
         public final byte[] data;
 
-        public SoundModel(UUID uuid, UUID vendorUuid, int type, byte[] data) {
-            this.uuid = uuid;
-            this.vendorUuid = vendorUuid;
+        public SoundModel(@NonNull UUID uuid, @Nullable UUID vendorUuid, int type,
+                @Nullable byte[] data) {
+            this.uuid = requireNonNull(uuid);
+            this.vendorUuid = vendorUuid != null ? vendorUuid : new UUID(0, 0);
             this.type = type;
-            this.data = data;
+            this.data = data != null ? data : new byte[0];
         }
 
         @Override
@@ -271,8 +287,6 @@
             if (!(obj instanceof SoundModel))
                 return false;
             SoundModel other = (SoundModel) obj;
-            if (!Arrays.equals(data, other.data))
-                return false;
             if (type != other.type)
                 return false;
             if (uuid == null) {
@@ -285,6 +299,8 @@
                     return false;
             } else if (!vendorUuid.equals(other.vendorUuid))
                 return false;
+            if (!Arrays.equals(data, other.data))
+                return false;
             return true;
         }
     }
@@ -306,24 +322,28 @@
 
         /** Locale of the keyphrase. JAVA Locale string e.g en_US */
         @UnsupportedAppUsage
+        @NonNull
         public final String locale;
 
         /** Key phrase text */
         @UnsupportedAppUsage
+        @NonNull
         public final String text;
 
         /** Users this key phrase has been trained for. countains sound trigger specific user IDs
          * derived from system user IDs {@link android.os.UserHandle#getIdentifier()}. */
         @UnsupportedAppUsage
+        @NonNull
         public final int[] users;
 
         @UnsupportedAppUsage
-        public Keyphrase(int id, int recognitionModes, String locale, String text, int[] users) {
+        public Keyphrase(int id, int recognitionModes, @NonNull String locale, @NonNull String text,
+                @Nullable int[] users) {
             this.id = id;
             this.recognitionModes = recognitionModes;
-            this.locale = locale;
-            this.text = text;
-            this.users = users;
+            this.locale = requireNonNull(locale);
+            this.text = requireNonNull(text);
+            this.users = users != null ? users : new int[0];
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<Keyphrase> CREATOR
@@ -427,13 +447,15 @@
     public static class KeyphraseSoundModel extends SoundModel implements Parcelable {
         /** Key phrases in this sound model */
         @UnsupportedAppUsage
+        @NonNull
         public final Keyphrase[] keyphrases; // keyword phrases in model
 
         @UnsupportedAppUsage
         public KeyphraseSoundModel(
-                UUID uuid, UUID vendorUuid, byte[] data, Keyphrase[] keyphrases) {
+                @NonNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data,
+                @Nullable Keyphrase[] keyphrases) {
             super(uuid, vendorUuid, TYPE_KEYPHRASE, data);
-            this.keyphrases = keyphrases;
+            this.keyphrases = keyphrases != null ? keyphrases : new Keyphrase[0];
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseSoundModel> CREATOR
@@ -528,7 +550,8 @@
         };
 
         @UnsupportedAppUsage
-        public GenericSoundModel(UUID uuid, UUID vendorUuid, byte[] data) {
+        public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
+                @Nullable byte[] data) {
             super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data);
         }
 
@@ -648,6 +671,12 @@
      * @hide
      */
     public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4;
+    /**
+     * Generic (non-speech) recognition.
+     *
+     * @hide
+     */
+    public static final int RECOGNITION_MODE_GENERIC = 0x8;
 
     /**
      *  Status codes for {@link RecognitionEvent}
@@ -739,6 +768,7 @@
          *
          * @hide
          */
+        @NonNull
         public final AudioFormat captureFormat;
         /**
          * Opaque data for use by system applications who know about voice engine internals,
@@ -747,13 +777,14 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @NonNull
         public final byte[] data;
 
         /** @hide */
         @UnsupportedAppUsage
         public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
                 int captureSession, int captureDelayMs, int capturePreambleMs,
-                boolean triggerInData, AudioFormat captureFormat, byte[] data) {
+                boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data) {
             this.status = status;
             this.soundModelHandle = soundModelHandle;
             this.captureAvailable = captureAvailable;
@@ -761,8 +792,8 @@
             this.captureDelayMs = captureDelayMs;
             this.capturePreambleMs = capturePreambleMs;
             this.triggerInData = triggerInData;
-            this.captureFormat = captureFormat;
-            this.data = data;
+            this.captureFormat = requireNonNull(captureFormat);
+            this.data = data != null ? data : new byte[0];
         }
 
         /**
@@ -965,19 +996,21 @@
         /** List of all keyphrases in the sound model for which recognition should be performed with
          * options for each keyphrase. */
         @UnsupportedAppUsage
+        @NonNull
         public final KeyphraseRecognitionExtra keyphrases[];
         /** Opaque data for use by system applications who know about voice engine internals,
          * typically during enrollment. */
         @UnsupportedAppUsage
+        @NonNull
         public final byte[] data;
 
         @UnsupportedAppUsage
         public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
-                KeyphraseRecognitionExtra[] keyphrases, byte[] data) {
+                @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) {
             this.captureRequested = captureRequested;
             this.allowMultipleTriggers = allowMultipleTriggers;
-            this.keyphrases = keyphrases;
-            this.data = data;
+            this.keyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0];
+            this.data = data != null ? data : new byte[0];
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<RecognitionConfig> CREATOR
@@ -1126,15 +1159,17 @@
         /** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
          * be recognized (RecognitionConfig) */
         @UnsupportedAppUsage
+        @NonNull
         public final ConfidenceLevel[] confidenceLevels;
 
         @UnsupportedAppUsage
         public KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel,
-                ConfidenceLevel[] confidenceLevels) {
+                @Nullable ConfidenceLevel[] confidenceLevels) {
             this.id = id;
             this.recognitionModes = recognitionModes;
             this.coarseConfidenceLevel = coarseConfidenceLevel;
-            this.confidenceLevels = confidenceLevels;
+            this.confidenceLevels =
+                    confidenceLevels != null ? confidenceLevels : new ConfidenceLevel[0];
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionExtra> CREATOR
@@ -1217,16 +1252,18 @@
     public static class KeyphraseRecognitionEvent extends RecognitionEvent implements Parcelable {
         /** Indicates if the key phrase is present in the buffered audio available for capture */
         @UnsupportedAppUsage
+        @NonNull
         public final KeyphraseRecognitionExtra[] keyphraseExtras;
 
         @UnsupportedAppUsage
         public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
                int captureSession, int captureDelayMs, int capturePreambleMs,
-               boolean triggerInData, AudioFormat captureFormat, byte[] data,
-               KeyphraseRecognitionExtra[] keyphraseExtras) {
+               boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
+               @Nullable KeyphraseRecognitionExtra[] keyphraseExtras) {
             super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
                   capturePreambleMs, triggerInData, captureFormat, data);
-            this.keyphraseExtras = keyphraseExtras;
+            this.keyphraseExtras =
+                    keyphraseExtras != null ? keyphraseExtras : new KeyphraseRecognitionExtra[0];
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionEvent> CREATOR
@@ -1343,8 +1380,8 @@
         @UnsupportedAppUsage
         public GenericRecognitionEvent(int status, int soundModelHandle,
                 boolean captureAvailable, int captureSession, int captureDelayMs,
-                int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat,
-                byte[] data) {
+                int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat,
+                @Nullable byte[] data) {
             super(status, soundModelHandle, captureAvailable, captureSession,
                     captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
                     data);
@@ -1408,13 +1445,14 @@
         /** The updated sound model handle */
         public final int soundModelHandle;
         /** New sound model data */
+        @NonNull
         public final byte[] data;
 
         @UnsupportedAppUsage
-        SoundModelEvent(int status, int soundModelHandle, byte[] data) {
+        SoundModelEvent(int status, int soundModelHandle, @Nullable byte[] data) {
             this.status = status;
             this.soundModelHandle = soundModelHandle;
-            this.data = data;
+            this.data = data != null ? data : new byte[0];
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<SoundModelEvent> CREATOR
@@ -1498,8 +1536,9 @@
      * @hide
      */
     public static final int SERVICE_STATE_DISABLED = 1;
-
-    /**
+    private static Object mServiceLock = new Object();
+    private static ISoundTriggerMiddlewareService mService;
+   /**
      * @return returns current package name.
      */
     static String getCurrentOpPackageName() {
@@ -1523,25 +1562,22 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public static int listModules(ArrayList<ModuleProperties> modules) {
-        return listModules(getCurrentOpPackageName(), modules);
+    public static int listModules(@NonNull ArrayList<ModuleProperties> modules) {
+        try {
+            SoundTriggerModuleDescriptor[] descs = getService().listModules();
+            modules.clear();
+            modules.ensureCapacity(descs.length);
+            for (SoundTriggerModuleDescriptor desc : descs) {
+                modules.add(ConversionUtil.aidl2apiModuleDescriptor(desc));
+            }
+            return STATUS_OK;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception caught", e);
+            return STATUS_DEAD_OBJECT;
+        }
     }
 
     /**
-     * Returns a list of descriptors for all hardware modules loaded.
-     * @param opPackageName
-     * @param modules A ModuleProperties array where the list will be returned.
-     * @return - {@link #STATUS_OK} in case of success
-     *         - {@link #STATUS_ERROR} in case of unspecified error
-     *         - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission
-     *         - {@link #STATUS_NO_INIT} if the native service cannot be reached
-     *         - {@link #STATUS_BAD_VALUE} if modules is null
-     *         - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
-     */
-    private static native int listModules(String opPackageName,
-                                          ArrayList<ModuleProperties> modules);
-
-    /**
      * Get an interface on a hardware module to control sound models and recognition on
      * this module.
      * @param moduleId Sound module system identifier {@link ModuleProperties#id}. mandatory.
@@ -1553,14 +1589,40 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public static SoundTriggerModule attachModule(int moduleId,
-                                                  StatusListener listener,
-                                                  Handler handler) {
-        if (listener == null) {
+    public static @NonNull SoundTriggerModule attachModule(int moduleId,
+            @NonNull StatusListener listener,
+            @Nullable Handler handler) {
+        Looper looper = handler != null ? handler.getLooper() : Looper.getMainLooper();
+        try {
+            return new SoundTriggerModule(getService(), moduleId, listener, looper);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
             return null;
         }
-        SoundTriggerModule module = new SoundTriggerModule(moduleId, listener, handler);
-        return module;
+    }
+
+    private static ISoundTriggerMiddlewareService getService() {
+        synchronized (mServiceLock) {
+            while (true) {
+                IBinder binder = null;
+                try {
+                    binder =
+                            ServiceManager.getServiceOrThrow(
+                                    Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE);
+                    binder.linkToDeath(() -> {
+                        synchronized (mServiceLock) {
+                            mService = null;
+                        }
+                    }, 0);
+                    mService = ISoundTriggerMiddlewareService.Stub.asInterface(binder);
+                    break;
+                } catch (Exception e) {
+                    Log.e(TAG, "Failed to bind to soundtrigger service", e);
+                }
+            }
+            return  mService;
+        }
+
     }
 
     /**
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index b16ef5c..7cf5600 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -16,14 +16,23 @@
 
 package android.hardware.soundtrigger;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
-import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
+import android.media.soundtrigger_middleware.ISoundTriggerCallback;
+import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
+import android.media.soundtrigger_middleware.ISoundTriggerModule;
+import android.media.soundtrigger_middleware.ModelParameterRange;
+import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger_middleware.PhraseSoundModel;
+import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger_middleware.SoundModel;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-
-import java.lang.ref.WeakReference;
+import android.os.RemoteException;
+import android.util.Log;
 
 /**
  * The SoundTriggerModule provides APIs to control sound models and sound detection
@@ -32,39 +41,47 @@
  * @hide
  */
 public class SoundTriggerModule {
-    @UnsupportedAppUsage
-    private long mNativeContext;
+    private static final String TAG = "SoundTriggerModule";
 
-    @UnsupportedAppUsage
-    private int mId;
-    private NativeEventHandlerDelegate mEventHandlerDelegate;
-
-    // to be kept in sync with core/jni/android_hardware_SoundTrigger.cpp
     private static final int EVENT_RECOGNITION = 1;
     private static final int EVENT_SERVICE_DIED = 2;
-    private static final int EVENT_SOUNDMODEL = 3;
-    private static final int EVENT_SERVICE_STATE_CHANGE = 4;
+    private static final int EVENT_SERVICE_STATE_CHANGE = 3;
+    @UnsupportedAppUsage
+    private int mId;
+    private EventHandlerDelegate mEventHandlerDelegate;
+    private ISoundTriggerModule mService;
 
-    SoundTriggerModule(int moduleId, SoundTrigger.StatusListener listener, Handler handler) {
+    SoundTriggerModule(@NonNull ISoundTriggerMiddlewareService service,
+            int moduleId, @NonNull SoundTrigger.StatusListener listener, @NonNull Looper looper)
+            throws RemoteException {
         mId = moduleId;
-        mEventHandlerDelegate = new NativeEventHandlerDelegate(listener, handler);
-        native_setup(SoundTrigger.getCurrentOpPackageName(),
-                new WeakReference<SoundTriggerModule>(this));
+        mEventHandlerDelegate = new EventHandlerDelegate(listener, looper);
+        mService = service.attach(moduleId, mEventHandlerDelegate);
+        mService.asBinder().linkToDeath(mEventHandlerDelegate, 0);
     }
-    private native void native_setup(String opPackageName, Object moduleThis);
 
     @Override
     protected void finalize() {
-        native_finalize();
+        detach();
     }
-    private native void native_finalize();
 
     /**
      * Detach from this module. The {@link SoundTrigger.StatusListener} callback will not be called
      * anymore and associated resources will be released.
-     * */
+     * All models must have been unloaded prior to detaching.
+     */
     @UnsupportedAppUsage
-    public native void detach();
+    public synchronized void detach() {
+        try {
+            if (mService != null) {
+                mService.asBinder().unlinkToDeath(mEventHandlerDelegate, 0);
+                mService.detach();
+                mService = null;
+            }
+        } catch (Exception e) {
+            handleException(e);
+        }
+    }
 
     /**
      * Load a {@link SoundTrigger.SoundModel} to the hardware. A sound model must be loaded in
@@ -82,7 +99,26 @@
      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
      */
     @UnsupportedAppUsage
-    public native int loadSoundModel(SoundTrigger.SoundModel model, int[] soundModelHandle);
+    public synchronized int loadSoundModel(@NonNull SoundTrigger.SoundModel model,
+            @NonNull int[] soundModelHandle) {
+        try {
+            if (model instanceof SoundTrigger.GenericSoundModel) {
+                SoundModel aidlModel = ConversionUtil.api2aidlGenericSoundModel(
+                        (SoundTrigger.GenericSoundModel) model);
+                soundModelHandle[0] = mService.loadModel(aidlModel);
+                return SoundTrigger.STATUS_OK;
+            }
+            if (model instanceof SoundTrigger.KeyphraseSoundModel) {
+                PhraseSoundModel aidlModel = ConversionUtil.api2aidlPhraseSoundModel(
+                        (SoundTrigger.KeyphraseSoundModel) model);
+                soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
+                return SoundTrigger.STATUS_OK;
+            }
+            return SoundTrigger.STATUS_BAD_VALUE;
+        } catch (Exception e) {
+            return handleException(e);
+        }
+    }
 
     /**
      * Unload a {@link SoundTrigger.SoundModel} and abort any pendiong recognition
@@ -97,7 +133,14 @@
      *         service fails
      */
     @UnsupportedAppUsage
-    public native int unloadSoundModel(int soundModelHandle);
+    public synchronized int unloadSoundModel(int soundModelHandle) {
+        try {
+            mService.unloadModel(soundModelHandle);
+            return SoundTrigger.STATUS_OK;
+        } catch (Exception e) {
+            return handleException(e);
+        }
+    }
 
     /**
      * Start listening to all key phrases in a {@link SoundTrigger.SoundModel}.
@@ -117,7 +160,16 @@
      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
      */
     @UnsupportedAppUsage
-    public native int startRecognition(int soundModelHandle, SoundTrigger.RecognitionConfig config);
+    public synchronized int startRecognition(int soundModelHandle,
+            SoundTrigger.RecognitionConfig config) {
+        try {
+            mService.startRecognition(soundModelHandle,
+                    ConversionUtil.api2aidlRecognitionConfig(config));
+            return SoundTrigger.STATUS_OK;
+        } catch (Exception e) {
+            return handleException(e);
+        }
+    }
 
     /**
      * Stop listening to all key phrases in a {@link SoundTrigger.SoundModel}
@@ -133,12 +185,20 @@
      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
      */
     @UnsupportedAppUsage
-    public native int stopRecognition(int soundModelHandle);
+    public synchronized int stopRecognition(int soundModelHandle) {
+        try {
+            mService.stopRecognition(soundModelHandle);
+            return SoundTrigger.STATUS_OK;
+        } catch (Exception e) {
+            return handleException(e);
+        }
+    }
 
     /**
      * Get the current state of a {@link SoundTrigger.SoundModel}.
-     * The state will be returned asynchronously as a {@link SoundTrigger#RecognitionEvent}
-     * in the callback registered in the {@link SoundTrigger.startRecognition} method.
+     * The state will be returned asynchronously as a {@link SoundTrigger.RecognitionEvent}
+     * in the callback registered in the
+     * {@link SoundTrigger#attachModule(int, SoundTrigger.StatusListener, Handler)} method.
      * @param soundModelHandle The sound model handle indicating which model's state to return
      * @return - {@link SoundTrigger#STATUS_OK} in case of success
      *         - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
@@ -150,46 +210,71 @@
      *         service fails
      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
      */
-    public native int getModelState(int soundModelHandle);
+    public synchronized int getModelState(int soundModelHandle) {
+        try {
+            mService.forceRecognitionEvent(soundModelHandle);
+            return SoundTrigger.STATUS_OK;
+        } catch (Exception e) {
+            return handleException(e);
+        }
+    }
 
     /**
      * Set a model specific {@link ModelParams} with the given value. This
-     * parameter will keep its value for the duration the model is loaded regardless of starting and
+     * parameter will keep its value for the duration the model is loaded regardless of starting
+     * and
      * stopping recognition. Once the model is unloaded, the value will be lost.
-     * {@link SoundTriggerModule#isParameterSupported} should be checked first before calling this
-     * method.
+     * {@link SoundTriggerModule#queryParameter(int, int)} should be checked first before calling
+     * this method.
      *
      * @param soundModelHandle handle of model to apply parameter
-     * @param modelParam   {@link ModelParams}
-     * @param value        Value to set
+     * @param modelParam       {@link ModelParams}
+     * @param value            Value to set
      * @return - {@link SoundTrigger#STATUS_OK} in case of success
-     *         - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
-     *         - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
-     *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
-     *           if API is not supported by HAL
+     * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+     * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
+     * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
+     * if API is not supported by HAL
      */
-    public native int setParameter(int soundModelHandle,
-            @ModelParams int modelParam, int value);
+    public synchronized int setParameter(int soundModelHandle, @ModelParams int modelParam,
+            int value) {
+        try {
+            mService.setModelParameter(soundModelHandle,
+                    ConversionUtil.api2aidlModelParameter(modelParam), value);
+            return SoundTrigger.STATUS_OK;
+        } catch (Exception e) {
+            return handleException(e);
+        }
+    }
 
     /**
      * Get a model specific {@link ModelParams}. This parameter will keep its value
      * for the duration the model is loaded regardless of starting and stopping recognition.
      * Once the model is unloaded, the value will be lost. If the value is not set, a default
      * value is returned. See {@link ModelParams} for parameter default values.
-     * {@link SoundTriggerModule#isParameterSupported} should be checked first before
+     * {@link SoundTriggerModule#queryParameter(int, int)} should be checked first before
      * calling this method. Otherwise, an exception can be thrown.
      *
      * @param soundModelHandle handle of model to get parameter
-     * @param modelParam   {@link ModelParams}
+     * @param modelParam       {@link ModelParams}
      * @return value of parameter
      * @throws UnsupportedOperationException if hal or model do not support this API.
-     *         {@link SoundTriggerModule#isParameterSupported} should be checked first.
-     * @throws IllegalArgumentException if invalid model handle or parameter is passed.
-     *         {@link SoundTriggerModule#isParameterSupported} should be checked first.
+     *                                       {@link SoundTriggerModule#queryParameter(int, int)}
+     *                                       should
+     *                                       be checked first.
+     * @throws IllegalArgumentException      if invalid model handle or parameter is passed.
+     *                                       {@link SoundTriggerModule#queryParameter(int, int)}
+     *                                       should be checked first.
      */
-    public native int getParameter(int soundModelHandle,
-            @ModelParams int modelParam)
-            throws UnsupportedOperationException, IllegalArgumentException;
+    public synchronized int getParameter(int soundModelHandle, @ModelParams int modelParam)
+            throws UnsupportedOperationException, IllegalArgumentException {
+        try {
+            return mService.getModelParameter(soundModelHandle,
+                    ConversionUtil.api2aidlModelParameter(modelParam));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 
     /**
      * Determine if parameter control is supported for the given model handle.
@@ -197,85 +282,98 @@
      * {@link SoundTriggerModule#getParameter}.
      *
      * @param soundModelHandle handle of model to get parameter
-     * @param modelParam {@link ModelParams}
+     * @param modelParam       {@link ModelParams}
      * @return supported range of parameter, null if not supported
      */
     @Nullable
-    public native ModelParamRange queryParameter(int soundModelHandle, @ModelParams int modelParam);
-
-    private class NativeEventHandlerDelegate {
-        private final Handler mHandler;
-
-        NativeEventHandlerDelegate(final SoundTrigger.StatusListener listener,
-                                   Handler handler) {
-            // find the looper for our new event handler
-            Looper looper;
-            if (handler != null) {
-                looper = handler.getLooper();
-            } else {
-                looper = Looper.getMainLooper();
-            }
-
-            // construct the event handler with this looper
-            if (looper != null) {
-                // implement the event handler delegate
-                mHandler = new Handler(looper) {
-                    @Override
-                    public void handleMessage(Message msg) {
-                        switch(msg.what) {
-                        case EVENT_RECOGNITION:
-                            if (listener != null) {
-                                listener.onRecognition(
-                                        (SoundTrigger.RecognitionEvent)msg.obj);
-                            }
-                            break;
-                        case EVENT_SOUNDMODEL:
-                            if (listener != null) {
-                                listener.onSoundModelUpdate(
-                                        (SoundTrigger.SoundModelEvent)msg.obj);
-                            }
-                            break;
-                        case EVENT_SERVICE_STATE_CHANGE:
-                            if (listener != null) {
-                                listener.onServiceStateChange(msg.arg1);
-                            }
-                            break;
-                        case EVENT_SERVICE_DIED:
-                            if (listener != null) {
-                                listener.onServiceDied();
-                            }
-                            break;
-                        default:
-                            break;
-                        }
-                    }
-                };
-            } else {
-                mHandler = null;
-            }
-        }
-
-        Handler handler() {
-            return mHandler;
+    public synchronized SoundTrigger.ModelParamRange queryParameter(int soundModelHandle,
+            @ModelParams int modelParam) {
+        try {
+            return ConversionUtil.aidl2apiModelParameterRange(mService.queryModelParameterSupport(
+                    soundModelHandle,
+                    ConversionUtil.api2aidlModelParameter(modelParam)));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
-    @SuppressWarnings("unused")
-    @UnsupportedAppUsage
-    private static void postEventFromNative(Object module_ref,
-                                            int what, int arg1, int arg2, Object obj) {
-        SoundTriggerModule module = (SoundTriggerModule)((WeakReference)module_ref).get();
-        if (module == null) {
-            return;
+    private int handleException(Exception e) {
+        Log.e(TAG, "", e);
+        if (e instanceof NullPointerException) {
+            return SoundTrigger.STATUS_NO_INIT;
+        }
+        if (e instanceof RemoteException) {
+            return SoundTrigger.STATUS_DEAD_OBJECT;
+        }
+        if (e instanceof IllegalArgumentException) {
+            return SoundTrigger.STATUS_BAD_VALUE;
+        }
+        if (e instanceof IllegalStateException) {
+            return SoundTrigger.STATUS_INVALID_OPERATION;
+        }
+        return SoundTrigger.STATUS_ERROR;
+    }
+
+    private class EventHandlerDelegate extends ISoundTriggerCallback.Stub implements
+            IBinder.DeathRecipient {
+        private final Handler mHandler;
+
+        EventHandlerDelegate(@NonNull final SoundTrigger.StatusListener listener,
+                @NonNull Looper looper) {
+
+            // construct the event handler with this looper
+            // implement the event handler delegate
+            mHandler = new Handler(looper) {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case EVENT_RECOGNITION:
+                            listener.onRecognition(
+                                    (SoundTrigger.RecognitionEvent) msg.obj);
+                            break;
+                        case EVENT_SERVICE_STATE_CHANGE:
+                            listener.onServiceStateChange(msg.arg1);
+                            break;
+                        case EVENT_SERVICE_DIED:
+                            listener.onServiceDied();
+                            break;
+                        default:
+                            Log.e(TAG, "Unknown message: " + msg.toString());
+                            break;
+                    }
+                }
+            };
         }
 
-        NativeEventHandlerDelegate delegate = module.mEventHandlerDelegate;
-        if (delegate != null) {
-            Handler handler = delegate.handler();
-            if (handler != null) {
-                Message m = handler.obtainMessage(what, arg1, arg2, obj);
-                handler.sendMessage(m);
-            }
+        @Override
+        public synchronized void onRecognition(int handle, RecognitionEvent event)
+                throws RemoteException {
+            Message m = mHandler.obtainMessage(EVENT_RECOGNITION,
+                    ConversionUtil.aidl2apiRecognitionEvent(handle, event));
+            mHandler.sendMessage(m);
+        }
+
+        @Override
+        public synchronized void onPhraseRecognition(int handle, PhraseRecognitionEvent event)
+                throws RemoteException {
+            Message m = mHandler.obtainMessage(EVENT_RECOGNITION,
+                    ConversionUtil.aidl2apiPhraseRecognitionEvent(handle, event));
+            mHandler.sendMessage(m);
+        }
+
+        @Override
+        public synchronized void onRecognitionAvailabilityChange(boolean available)
+                throws RemoteException {
+            Message m = mHandler.obtainMessage(EVENT_SERVICE_STATE_CHANGE,
+                    available ? SoundTrigger.SERVICE_STATE_ENABLED
+                            : SoundTrigger.SERVICE_STATE_DISABLED);
+            mHandler.sendMessage(m);
+        }
+
+        @Override
+        public synchronized void binderDied() {
+            Message m = mHandler.obtainMessage(EVENT_SERVICE_DIED);
+            mHandler.sendMessage(m);
         }
     }
 }
diff --git a/core/java/android/net/InvalidPacketException.java b/core/java/android/net/InvalidPacketException.java
new file mode 100644
index 0000000..909998d
--- /dev/null
+++ b/core/java/android/net/InvalidPacketException.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 android.net;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Thrown when a packet is invalid.
+ * @hide
+ */
+@SystemApi
+public class InvalidPacketException extends Exception {
+    public final int error;
+
+    // Must match SocketKeepalive#ERROR_INVALID_IP_ADDRESS.
+    /** Invalid IP address. */
+    public static final int ERROR_INVALID_IP_ADDRESS = -21;
+
+    // Must match SocketKeepalive#ERROR_INVALID_PORT.
+    /** Invalid port number. */
+    public static final int ERROR_INVALID_PORT = -22;
+
+    // Must match SocketKeepalive#ERROR_INVALID_LENGTH.
+    /** Invalid packet length. */
+    public static final int ERROR_INVALID_LENGTH = -23;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "ERROR_" }, value = {
+        ERROR_INVALID_IP_ADDRESS,
+        ERROR_INVALID_PORT,
+        ERROR_INVALID_LENGTH
+    })
+    public @interface ErrorCode {}
+
+    /**
+     * This packet is invalid.
+     * See the error code for details.
+     */
+    public InvalidPacketException(@ErrorCode final int error) {
+        this.error = error;
+    }
+}
diff --git a/core/java/android/net/KeepalivePacketData.java b/core/java/android/net/KeepalivePacketData.java
index 9b8b732..2b8b7e6 100644
--- a/core/java/android/net/KeepalivePacketData.java
+++ b/core/java/android/net/KeepalivePacketData.java
@@ -16,13 +16,13 @@
 
 package android.net;
 
-import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
-import static android.net.SocketKeepalive.ERROR_INVALID_PORT;
+import static android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS;
+import static android.net.InvalidPacketException.ERROR_INVALID_PORT;
 
-import android.net.SocketKeepalive.InvalidPacketException;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.net.util.IpUtils;
 import android.os.Parcel;
-import android.os.Parcelable;
 import android.util.Log;
 
 import java.net.InetAddress;
@@ -33,13 +33,16 @@
  *
  * @hide
  */
-public class KeepalivePacketData implements Parcelable {
+@SystemApi
+public class KeepalivePacketData {
     private static final String TAG = "KeepalivePacketData";
 
     /** Source IP address */
+    @NonNull
     public final InetAddress srcAddress;
 
     /** Destination IP address */
+    @NonNull
     public final InetAddress dstAddress;
 
     /** Source port */
@@ -51,13 +54,14 @@
     /** Packet data. A raw byte string of packet data, not including the link-layer header. */
     private final byte[] mPacket;
 
-    protected static final int IPV4_HEADER_LENGTH = 20;
-    protected static final int UDP_HEADER_LENGTH = 8;
-
     // This should only be constructed via static factory methods, such as
-    // nattKeepalivePacket
-    protected KeepalivePacketData(InetAddress srcAddress, int srcPort,
-            InetAddress dstAddress, int dstPort, byte[] data) throws InvalidPacketException {
+    // nattKeepalivePacket.
+    /**
+     * A holding class for data necessary to build a keepalive packet.
+     */
+    protected KeepalivePacketData(@NonNull InetAddress srcAddress, int srcPort,
+            @NonNull InetAddress dstAddress, int dstPort,
+                    @NonNull byte[] data) throws InvalidPacketException {
         this.srcAddress = srcAddress;
         this.dstAddress = dstAddress;
         this.srcPort = srcPort;
@@ -78,16 +82,12 @@
         }
     }
 
+    @NonNull
     public byte[] getPacket() {
         return mPacket.clone();
     }
 
-    /* Parcelable Implementation */
-    public int describeContents() {
-        return 0;
-    }
-
-    /** Write to parcel */
+    /** @hide */
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(srcAddress.getHostAddress());
         out.writeString(dstAddress.getHostAddress());
@@ -96,6 +96,7 @@
         out.writeByteArray(mPacket);
     }
 
+    /** @hide */
     protected KeepalivePacketData(Parcel in) {
         srcAddress = NetworkUtils.numericToInetAddress(in.readString());
         dstAddress = NetworkUtils.numericToInetAddress(in.readString());
@@ -103,17 +104,4 @@
         dstPort = in.readInt();
         mPacket = in.createByteArray();
     }
-
-    /** Parcelable Creator */
-    public static final @android.annotation.NonNull Parcelable.Creator<KeepalivePacketData> CREATOR =
-            new Parcelable.Creator<KeepalivePacketData>() {
-                public KeepalivePacketData createFromParcel(Parcel in) {
-                    return new KeepalivePacketData(in);
-                }
-
-                public KeepalivePacketData[] newArray(int size) {
-                    return new KeepalivePacketData[size];
-                }
-            };
-
 }
diff --git a/core/java/android/net/NattKeepalivePacketData.java b/core/java/android/net/NattKeepalivePacketData.java
index a77c244..3fb52f1 100644
--- a/core/java/android/net/NattKeepalivePacketData.java
+++ b/core/java/android/net/NattKeepalivePacketData.java
@@ -19,7 +19,6 @@
 import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
 import static android.net.SocketKeepalive.ERROR_INVALID_PORT;
 
-import android.net.SocketKeepalive.InvalidPacketException;
 import android.net.util.IpUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -32,6 +31,9 @@
 
 /** @hide */
 public final class NattKeepalivePacketData extends KeepalivePacketData implements Parcelable {
+    private static final int IPV4_HEADER_LENGTH = 20;
+    private static final int UDP_HEADER_LENGTH = 8;
+
     // This should only be constructed via static factory methods, such as
     // nattKeepalivePacket
     private NattKeepalivePacketData(InetAddress srcAddress, int srcPort,
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index ec73866..fb224fb 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -147,17 +147,6 @@
         }
     }
 
-    /**
-     * This packet is invalid.
-     * See the error code for details.
-     * @hide
-     */
-    public static class InvalidPacketException extends ErrorCodeException {
-        public InvalidPacketException(final int error) {
-            super(error);
-        }
-    }
-
     @NonNull final IConnectivityManager mService;
     @NonNull final Network mNetwork;
     @NonNull final ParcelFileDescriptor mPfd;
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 12bce8a..ed980f3 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -182,6 +182,14 @@
     public static final int MAX_IPC_SIZE = 64 * 1024;
 
     /**
+     * Limit that should be placed on IPC sizes, in bytes, to keep them safely under the transaction
+     * buffer limit.
+     */
+    static int getSuggestedMaxIpcSizeBytes() {
+        return MAX_IPC_SIZE;
+    }
+
+    /**
      * Get the canonical name of the interface supported by this binder.
      */
     public @Nullable String getInterfaceDescriptor() throws RemoteException;
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 9eb6445..339397b 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -1815,8 +1815,12 @@
         p.writeToParcel(this, parcelableFlags);
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * Flatten the name of the class of the Parcelable into this Parcel.
+     *
+     * @param p The Parcelable object to be written.
+     * @see #readParcelableCreator
+     */
     public final void writeParcelableCreator(@NonNull Parcelable p) {
         String name = p.getClass().getName();
         writeString(name);
@@ -3011,8 +3015,19 @@
         return (T) creator.createFromParcel(this);
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * Read and return a Parcelable.Creator from the parcel. The given class loader will be used to
+     * load the {@link Parcelable.Creator}. If it is null, the default class loader will be used.
+     *
+     * @param loader A ClassLoader from which to instantiate the {@link Parcelable.Creator}
+     * object, or null for the default class loader.
+     * @return the previously written {@link Parcelable.Creator}, or null if a null Creator was
+     * written.
+     * @throws BadParcelableException Throws BadParcelableException if there was an error trying to
+     * read the {@link Parcelable.Creator}.
+     *
+     * @see #writeParcelableCreator
+     */
     @Nullable
     public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
         String name = readString();
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 725e0fb..5e478b5 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -44,70 +44,10 @@
  * <p>
  * <b>Device battery life will be significantly affected by the use of this API.</b>
  * Do not acquire {@link WakeLock}s unless you really need them, use the minimum levels
- * possible, and be sure to release them as soon as possible.
- * </p><p>
- * The primary API you'll use is {@link #newWakeLock(int, String) newWakeLock()}.
- * This will create a {@link PowerManager.WakeLock} object.  You can then use methods
- * on the wake lock object to control the power state of the device.
- * </p><p>
- * In practice it's quite simple:
- * {@samplecode
- * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
- * PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
- * wl.acquire();
- *   ..screen will stay on during this section..
- * wl.release();
- * }
- * </p><p>
- * The following wake lock levels are defined, with varying effects on system power.
- * <i>These levels are mutually exclusive - you may only specify one of them.</i>
+ * possible, and be sure to release them as soon as possible. In most cases,
+ * you'll want to use
+ * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead.
  *
- * <table>
- *     <tr><th>Flag Value</th>
- *     <th>CPU</th> <th>Screen</th> <th>Keyboard</th></tr>
- *
- *     <tr><td>{@link #PARTIAL_WAKE_LOCK}</td>
- *         <td>On*</td> <td>Off</td> <td>Off</td>
- *     </tr>
- *
- *     <tr><td>{@link #SCREEN_DIM_WAKE_LOCK}</td>
- *         <td>On</td> <td>Dim</td> <td>Off</td>
- *     </tr>
- *
- *     <tr><td>{@link #SCREEN_BRIGHT_WAKE_LOCK}</td>
- *         <td>On</td> <td>Bright</td> <td>Off</td>
- *     </tr>
- *
- *     <tr><td>{@link #FULL_WAKE_LOCK}</td>
- *         <td>On</td> <td>Bright</td> <td>Bright</td>
- *     </tr>
- * </table>
- * </p><p>
- * *<i>If you hold a partial wake lock, the CPU will continue to run, regardless of any
- * display timeouts or the state of the screen and even after the user presses the power button.
- * In all other wake locks, the CPU will run, but the user can still put the device to sleep
- * using the power button.</i>
- * </p><p>
- * In addition, you can add two more flags, which affect behavior of the screen only.
- * <i>These flags have no effect when combined with a {@link #PARTIAL_WAKE_LOCK}.</i></p>
- *
- * <table>
- *     <tr><th>Flag Value</th> <th>Description</th></tr>
- *
- *     <tr><td>{@link #ACQUIRE_CAUSES_WAKEUP}</td>
- *         <td>Normal wake locks don't actually turn on the illumination.  Instead, they cause
- *         the illumination to remain on once it turns on (e.g. from user activity).  This flag
- *         will force the screen and/or keyboard to turn on immediately, when the WakeLock is
- *         acquired.  A typical use would be for notifications which are important for the user to
- *         see immediately.</td>
- *     </tr>
- *
- *     <tr><td>{@link #ON_AFTER_RELEASE}</td>
- *         <td>If this flag is set, the user activity timer will be reset when the WakeLock is
- *         released, causing the illumination to remain on a bit longer.  This can be used to
- *         reduce flicker if you are cycling between wake lock conditions.</td>
- *     </tr>
- * </table>
  * <p>
  * Any application using a WakeLock must request the {@code android.permission.WAKE_LOCK}
  * permission in an {@code <uses-permission>} element of the application's manifest.
@@ -931,7 +871,8 @@
      * {@link #FULL_WAKE_LOCK}, {@link #SCREEN_DIM_WAKE_LOCK}
      * and {@link #SCREEN_BRIGHT_WAKE_LOCK}.  Exactly one wake lock level must be
      * specified as part of the {@code levelAndFlags} parameter.
-     * </p><p>
+     * </p>
+     * <p>
      * The wake lock flags are: {@link #ACQUIRE_CAUSES_WAKEUP}
      * and {@link #ON_AFTER_RELEASE}.  Multiple flags can be combined as part of the
      * {@code levelAndFlags} parameters.
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index eb1684f..c86c83c 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -2381,7 +2381,11 @@
          * This id is provided by its own data source, and can be used to backup metadata
          * to the server.
          * This should be unique within each set of account_name/account_type/data_set
+         *
+         * @deprecated This column is no longer supported as of Android version
+         * {@link android.os.Build.VERSION_CODES#R}.
          */
+        @Deprecated
         public static final String BACKUP_ID = "backup_id";
 
         /**
@@ -2445,7 +2449,11 @@
          * Flag indicating that a raw contact's metadata has changed, and its metadata
          * needs to be synchronized by the server.
          * <P>Type: INTEGER (boolean)</P>
+         *
+         * @deprecated This column is no longer supported as of Android version
+         * {@link android.os.Build.VERSION_CODES#R}.
          */
+        @Deprecated
         public static final String METADATA_DIRTY = "metadata_dirty";
     }
 
@@ -4188,7 +4196,10 @@
          * Hash id on the data fields, used for backup and restore.
          *
          * @hide
+         * @deprecated This column is no longer supported as of Android version
+         * {@link android.os.Build.VERSION_CODES#R}.
          */
+        @Deprecated
         public static final String HASH_ID = "hash_id";
 
         /**
@@ -9495,7 +9506,10 @@
 
     /**
      * @hide
+     * @deprecated These columns are no longer supported as of Android version
+     * {@link android.os.Build.VERSION_CODES#R}.
      */
+    @Deprecated
     @SystemApi
     protected interface MetadataSyncColumns {
 
@@ -9602,7 +9616,10 @@
      * from server before it is merged into other CP2 tables.
      *
      * @hide
+     * @deprecated These columns are no longer supported as of Android version
+     * {@link android.os.Build.VERSION_CODES#R}.
      */
+    @Deprecated
     @SystemApi
     public static final class MetadataSync implements BaseColumns, MetadataSyncColumns {
 
@@ -9638,7 +9655,10 @@
 
     /**
      * @hide
+     * @deprecated These columns are no longer supported as of Android version
+     * {@link android.os.Build.VERSION_CODES#R}.
      */
+    @Deprecated
     @SystemApi
     protected interface MetadataSyncStateColumns {
 
@@ -9672,7 +9692,10 @@
      * sync state for a set of accounts.
      *
      * @hide
+     * @deprecated These columns are no longer supported as of Android version
+     * {@link android.os.Build.VERSION_CODES#R}.
      */
+    @Deprecated
     @SystemApi
     public static final class MetadataSyncState implements BaseColumns, MetadataSyncStateColumns {
 
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 2fa3386..701ba91 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -32,6 +32,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.AppGlobals;
+import android.app.PendingIntent;
 import android.content.ClipData;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
@@ -86,6 +87,7 @@
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
@@ -162,6 +164,15 @@
     /** {@hide} */
     public static final String SUICIDE_CALL = "suicide";
 
+    /** {@hide} */
+    public static final String CREATE_WRITE_REQUEST_CALL = "create_write_request";
+    /** {@hide} */
+    public static final String CREATE_TRASH_REQUEST_CALL = "create_trash_request";
+    /** {@hide} */
+    public static final String CREATE_FAVORITE_REQUEST_CALL = "create_favorite_request";
+    /** {@hide} */
+    public static final String CREATE_DELETE_REQUEST_CALL = "create_delete_request";
+
     /**
      * Extra used with {@link #SCAN_FILE_CALL} or {@link #SCAN_VOLUME_CALL} to indicate that
      * the file path originated from shell.
@@ -199,6 +210,13 @@
     /** {@hide} */
     public static final String DELETE_CONTRIBUTED_MEDIA_CALL = "delete_contributed_media";
 
+    /** {@hide} */
+    public static final String EXTRA_CLIP_DATA = "clip_data";
+    /** {@hide} */
+    public static final String EXTRA_CONTENT_VALUES = "content_values";
+    /** {@hide} */
+    public static final String EXTRA_RESULT = "result";
+
     /**
      * This is for internal use by the media scanner only.
      * Name of the (optional) Uri parameter that determines whether to skip deleting
@@ -606,6 +624,10 @@
      * {@link ContentResolver#delete}.
      * <p>
      * By default, trashed items are filtered away from operations.
+     *
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#QUERY_ARG_MATCH_TRASHED
+     * @see MediaStore#createTrashRequest
      */
     @Match
     public static final String QUERY_ARG_MATCH_TRASHED = "android:query-arg-match-trashed";
@@ -620,6 +642,10 @@
      * <p>
      * By default, favorite items are <em>not</em> filtered away from
      * operations.
+     *
+     * @see MediaColumns#IS_FAVORITE
+     * @see MediaStore#QUERY_ARG_MATCH_FAVORITE
+     * @see MediaStore#createFavoriteRequest
      */
     @Match
     public static final String QUERY_ARG_MATCH_FAVORITE = "android:query-arg-match-favorite";
@@ -952,7 +978,9 @@
      * @see MediaStore#setIncludeTrashed(Uri)
      * @see MediaStore#trash(Context, Uri)
      * @see MediaStore#untrash(Context, Uri)
+     * @removed
      */
+    @Deprecated
     public static void trash(@NonNull Context context, @NonNull Uri uri) {
         trash(context, uri, 48 * DateUtils.HOUR_IN_MILLIS);
     }
@@ -970,7 +998,9 @@
      * @see MediaStore#setIncludeTrashed(Uri)
      * @see MediaStore#trash(Context, Uri)
      * @see MediaStore#untrash(Context, Uri)
+     * @removed
      */
+    @Deprecated
     public static void trash(@NonNull Context context, @NonNull Uri uri,
             @DurationMillisLong long timeoutMillis) {
         if (timeoutMillis < 0) {
@@ -992,7 +1022,9 @@
      * @see MediaStore#setIncludeTrashed(Uri)
      * @see MediaStore#trash(Context, Uri)
      * @see MediaStore#untrash(Context, Uri)
+     * @removed
      */
+    @Deprecated
     public static void untrash(@NonNull Context context, @NonNull Uri uri) {
         final ContentValues values = new ContentValues();
         values.put(MediaColumns.IS_TRASHED, 0);
@@ -1010,6 +1042,180 @@
         return uri.buildUpon().authority(MediaStore.AUTHORITY_LEGACY).build();
     }
 
+    private static @NonNull PendingIntent createRequest(@NonNull ContentResolver resolver,
+            @NonNull String method, @NonNull Collection<Uri> uris, @Nullable ContentValues values) {
+        Objects.requireNonNull(resolver);
+        Objects.requireNonNull(uris);
+
+        final Iterator<Uri> it = uris.iterator();
+        final ClipData clipData = ClipData.newRawUri(null, it.next());
+        while (it.hasNext()) {
+            clipData.addItem(new ClipData.Item(it.next()));
+        }
+
+        final Bundle extras = new Bundle();
+        extras.putParcelable(EXTRA_CLIP_DATA, clipData);
+        extras.putParcelable(EXTRA_CONTENT_VALUES, values);
+        return resolver.call(AUTHORITY, method, null, extras).getParcelable(EXTRA_RESULT);
+    }
+
+    /**
+     * Create a {@link PendingIntent} that will prompt the user to grant your
+     * app write access for the requested media items.
+     * <p>
+     * This call only generates the request for a prompt; to display the prompt,
+     * call {@link Activity#startIntentSenderForResult} with
+     * {@link PendingIntent#getIntentSender()}. You can then determine if the
+     * user granted your request by testing for {@link Activity#RESULT_OK} in
+     * {@link Activity#onActivityResult}.
+     * <p>
+     * Permissions granted through this mechanism are tied to the lifecycle of
+     * the {@link Activity} that requests them. If you need to retain
+     * longer-term access for background actions, you can place items into a
+     * {@link ClipData} or {@link Intent} which can then be passed to
+     * {@link Context#startService} or
+     * {@link android.app.job.JobInfo.Builder#setClipData}. Be sure to include
+     * any relevant access modes you want to retain, such as
+     * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
+     * <p>
+     * The displayed prompt will reflect all the media items you're requesting,
+     * including those for which you already hold write access. If you want to
+     * determine if you already hold write access before requesting access, use
+     * {@code ContentResolver#checkUriPermission(Uri, int, int)} with
+     * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
+     * <p>
+     * For security and performance reasons this method does not support
+     * {@link Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION} or
+     * {@link Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}.
+     *
+     * @param resolver Used to connect with {@link MediaStore#AUTHORITY}.
+     *            Typically this value is {@link Context#getContentResolver()},
+     *            but if you need more explicit lifecycle controls, you can
+     *            obtain a {@link ContentProviderClient} and wrap it using
+     *            {@link ContentResolver#wrap(ContentProviderClient)}.
+     * @param uris The set of media items to include in this request. Each item
+     *            must be hosted by {@link MediaStore#AUTHORITY} and must
+     *            reference a specific media item by {@link BaseColumns#_ID}.
+     */
+    public static @NonNull PendingIntent createWriteRequest(@NonNull ContentResolver resolver,
+            @NonNull Collection<Uri> uris) {
+        return createRequest(resolver, CREATE_WRITE_REQUEST_CALL, uris, null);
+    }
+
+    /**
+     * Create a {@link PendingIntent} that will prompt the user to trash the
+     * requested media items. When the user approves this request,
+     * {@link MediaColumns#IS_TRASHED} is set on these items.
+     * <p>
+     * This call only generates the request for a prompt; to display the prompt,
+     * call {@link Activity#startIntentSenderForResult} with
+     * {@link PendingIntent#getIntentSender()}. You can then determine if the
+     * user granted your request by testing for {@link Activity#RESULT_OK} in
+     * {@link Activity#onActivityResult}.
+     * <p>
+     * The displayed prompt will reflect all the media items you're requesting,
+     * including those for which you already hold write access. If you want to
+     * determine if you already hold write access before requesting access, use
+     * {@code ContentResolver#checkUriPermission(Uri, int, int)} with
+     * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
+     *
+     * @param resolver Used to connect with {@link MediaStore#AUTHORITY}.
+     *            Typically this value is {@link Context#getContentResolver()},
+     *            but if you need more explicit lifecycle controls, you can
+     *            obtain a {@link ContentProviderClient} and wrap it using
+     *            {@link ContentResolver#wrap(ContentProviderClient)}.
+     * @param uris The set of media items to include in this request. Each item
+     *            must be hosted by {@link MediaStore#AUTHORITY} and must
+     *            reference a specific media item by {@link BaseColumns#_ID}.
+     * @param value The {@link MediaColumns#IS_TRASHED} value to apply.
+     * @see MediaColumns#IS_TRASHED
+     * @see MediaStore#QUERY_ARG_MATCH_TRASHED
+     */
+    public static @NonNull PendingIntent createTrashRequest(@NonNull ContentResolver resolver,
+            @NonNull Collection<Uri> uris, boolean value) {
+        final ContentValues values = new ContentValues();
+        if (value) {
+            values.put(MediaColumns.IS_TRASHED, 1);
+            values.put(MediaColumns.DATE_EXPIRES,
+                    (System.currentTimeMillis() + DateUtils.WEEK_IN_MILLIS) / 1000);
+        } else {
+            values.put(MediaColumns.IS_TRASHED, 0);
+            values.putNull(MediaColumns.DATE_EXPIRES);
+        }
+        return createRequest(resolver, CREATE_TRASH_REQUEST_CALL, uris, values);
+    }
+
+    /**
+     * Create a {@link PendingIntent} that will prompt the user to favorite the
+     * requested media items. When the user approves this request,
+     * {@link MediaColumns#IS_FAVORITE} is set on these items.
+     * <p>
+     * This call only generates the request for a prompt; to display the prompt,
+     * call {@link Activity#startIntentSenderForResult} with
+     * {@link PendingIntent#getIntentSender()}. You can then determine if the
+     * user granted your request by testing for {@link Activity#RESULT_OK} in
+     * {@link Activity#onActivityResult}.
+     * <p>
+     * The displayed prompt will reflect all the media items you're requesting,
+     * including those for which you already hold write access. If you want to
+     * determine if you already hold write access before requesting access, use
+     * {@code ContentResolver#checkUriPermission(Uri, int, int)} with
+     * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
+     *
+     * @param resolver Used to connect with {@link MediaStore#AUTHORITY}.
+     *            Typically this value is {@link Context#getContentResolver()},
+     *            but if you need more explicit lifecycle controls, you can
+     *            obtain a {@link ContentProviderClient} and wrap it using
+     *            {@link ContentResolver#wrap(ContentProviderClient)}.
+     * @param uris The set of media items to include in this request. Each item
+     *            must be hosted by {@link MediaStore#AUTHORITY} and must
+     *            reference a specific media item by {@link BaseColumns#_ID}.
+     * @param value The {@link MediaColumns#IS_FAVORITE} value to apply.
+     * @see MediaColumns#IS_FAVORITE
+     * @see MediaStore#QUERY_ARG_MATCH_FAVORITE
+     */
+    public static @NonNull PendingIntent createFavoriteRequest(@NonNull ContentResolver resolver,
+            @NonNull Collection<Uri> uris, boolean value) {
+        final ContentValues values = new ContentValues();
+        if (value) {
+            values.put(MediaColumns.IS_FAVORITE, 1);
+        } else {
+            values.put(MediaColumns.IS_FAVORITE, 0);
+        }
+        return createRequest(resolver, CREATE_FAVORITE_REQUEST_CALL, uris, values);
+    }
+
+    /**
+     * Create a {@link PendingIntent} that will prompt the user to permanently
+     * delete the requested media items. When the user approves this request,
+     * {@link ContentResolver#delete} will be called on these items.
+     * <p>
+     * This call only generates the request for a prompt; to display the prompt,
+     * call {@link Activity#startIntentSenderForResult} with
+     * {@link PendingIntent#getIntentSender()}. You can then determine if the
+     * user granted your request by testing for {@link Activity#RESULT_OK} in
+     * {@link Activity#onActivityResult}.
+     * <p>
+     * The displayed prompt will reflect all the media items you're requesting,
+     * including those for which you already hold write access. If you want to
+     * determine if you already hold write access before requesting access, use
+     * {@code ContentResolver#checkUriPermission(Uri, int, int)} with
+     * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
+     *
+     * @param resolver Used to connect with {@link MediaStore#AUTHORITY}.
+     *            Typically this value is {@link Context#getContentResolver()},
+     *            but if you need more explicit lifecycle controls, you can
+     *            obtain a {@link ContentProviderClient} and wrap it using
+     *            {@link ContentResolver#wrap(ContentProviderClient)}.
+     * @param uris The set of media items to include in this request. Each item
+     *            must be hosted by {@link MediaStore#AUTHORITY} and must
+     *            reference a specific media item by {@link BaseColumns#_ID}.
+     */
+    public static @NonNull PendingIntent createDeleteRequest(@NonNull ContentResolver resolver,
+            @NonNull Collection<Uri> uris) {
+        return createRequest(resolver, CREATE_DELETE_REQUEST_CALL, uris, null);
+    }
+
     /**
      * Common media metadata columns.
      */
@@ -1127,9 +1333,9 @@
          * Trashed items are retained until they expire as defined by
          * {@link #DATE_EXPIRES}.
          *
+         * @see MediaColumns#IS_TRASHED
          * @see MediaStore#QUERY_ARG_MATCH_TRASHED
-         * @see MediaStore#trash(Context, Uri)
-         * @see MediaStore#untrash(Context, Uri)
+         * @see MediaStore#createTrashRequest
          */
         @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_TRASHED = "is_trashed";
@@ -1302,7 +1508,9 @@
          * Flag indicating if the media item has been marked as being a
          * "favorite" by the user.
          *
+         * @see MediaColumns#IS_FAVORITE
          * @see MediaStore#QUERY_ARG_MATCH_FAVORITE
+         * @see MediaStore#createFavoriteRequest
          */
         @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_FAVORITE = "is_favorite";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 197d814..e73a74f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8392,6 +8392,20 @@
                 "navigation_mode";
 
         /**
+         * Scale factor for the back gesture inset size on the left side of the screen.
+         * @hide
+         */
+        public static final String BACK_GESTURE_INSET_SCALE_LEFT =
+                "back_gesture_inset_scale_left";
+
+        /**
+         * Scale factor for the back gesture inset size on the right side of the screen.
+         * @hide
+         */
+        public static final String BACK_GESTURE_INSET_SCALE_RIGHT =
+                "back_gesture_inset_scale_right";
+
+        /**
          * Controls whether aware is enabled.
          * @hide
          */
@@ -8416,6 +8430,12 @@
         public static final String PEOPLE_STRIP = "people_strip";
 
         /**
+         * Controls if window magnification is enabled.
+         * @hide
+         */
+        public static final String WINDOW_MAGNIFICATION = "window_magnification";
+
+        /**
          * Keys we no longer back up under the current schema, but want to continue to
          * process when restoring historical backup datasets.
          *
@@ -14229,6 +14249,20 @@
      */
     public static final int ADD_WIFI_RESULT_ALREADY_EXISTS = 2;
 
+    /**
+     * Activity Action: Allows user to select current bug report handler.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_BUGREPORT_HANDLER_SETTINGS =
+            "android.settings.BUGREPORT_HANDLER_SETTINGS";
+
     private static final String[] PM_WRITE_SETTINGS = {
         android.Manifest.permission.WRITE_SETTINGS
     };
diff --git a/core/java/android/service/dreams/Sandman.java b/core/java/android/service/dreams/Sandman.java
index efb8923..f2cedbc 100644
--- a/core/java/android/service/dreams/Sandman.java
+++ b/core/java/android/service/dreams/Sandman.java
@@ -36,12 +36,6 @@
 public final class Sandman {
     private static final String TAG = "Sandman";
 
-    // The component name of a special dock app that merely launches a dream.
-    // We don't want to launch this app when docked because it causes an unnecessary
-    // activity transition.  We just want to start the dream.
-    private static final ComponentName SOMNAMBULATOR_COMPONENT =
-            new ComponentName("com.android.systemui", "com.android.systemui.Somnambulator");
-
 
     // The sandman is eternal.  No one instantiates him.
     private Sandman() {
@@ -52,8 +46,11 @@
      * False if we should dream instead, if appropriate.
      */
     public static boolean shouldStartDockApp(Context context, Intent intent) {
+        final ComponentName somnambulatorComponent = ComponentName.unflattenFromString(
+                context.getResources().getString(
+                        com.android.internal.R.string.config_somnambulatorComponent));
         ComponentName name = intent.resolveActivity(context.getPackageManager());
-        return name != null && !name.equals(SOMNAMBULATOR_COMPONENT);
+        return name != null && !name.equals(somnambulatorComponent);
     }
 
     /**
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 8ab687f..c84fbc7 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -124,6 +124,13 @@
     public static final String KEY_IMPORTANCE = "key_importance";
 
     /**
+     * Data type: float, a ranking score from 0 (lowest) to 1 (highest).
+     * Used to rank notifications inside that fall under the same classification (i.e. alerting,
+     * silenced).
+     */
+    public static final String KEY_RANKING_SCORE = "key_ranking_score";
+
+    /**
      * Create a notification adjustment.
      *
      * @param pkg The package of the notification.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 85f13d5..c04ac59 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1895,7 +1895,8 @@
                     && ((mSmartActions == null ? 0 : mSmartActions.size())
                         == (other.mSmartActions == null ? 0 : other.mSmartActions.size()))
                     && Objects.equals(mSmartReplies, other.mSmartReplies)
-                    && Objects.equals(mCanBubble, other.mCanBubble);
+                    && Objects.equals(mCanBubble, other.mCanBubble)
+                    && Objects.equals(mVisuallyInterruptive, other.mVisuallyInterruptive);
         }
     }
 
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index dd2586c..d0675ed 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -481,9 +481,12 @@
      * as true on their TileService Manifest declaration, and will do nothing otherwise.
      */
     public static final void requestListeningState(Context context, ComponentName component) {
+        final ComponentName sysuiComponent = ComponentName.unflattenFromString(
+                context.getResources().getString(
+                        com.android.internal.R.string.config_systemUIServiceComponent));
         Intent intent = new Intent(ACTION_REQUEST_LISTENING);
         intent.putExtra(Intent.EXTRA_COMPONENT_NAME, component);
-        intent.setPackage("com.android.systemui");
+        intent.setPackage(sysuiComponent.getPackageName());
         context.sendBroadcast(intent, Manifest.permission.BIND_QUICK_SETTINGS_TILE);
     }
 }
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 716a522..51a9c86 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -35,8 +35,8 @@
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsReasonInfo;
 
-import com.android.internal.telephony.IPhoneStateListener;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IPhoneStateListener;
 
 import dalvik.system.VMRuntime;
 
@@ -169,14 +169,6 @@
     public static final int LISTEN_SIGNAL_STRENGTHS                         = 0x00000100;
 
     /**
-     * Listen for changes to OTASP mode.
-     *
-     * @see #onOtaspChanged
-     * @hide
-     */
-    public static final int LISTEN_OTASP_CHANGED                            = 0x00000200;
-
-    /**
      * Listen for changes to observed cell info.
      *
      * @see #onCellInfoChanged
@@ -196,12 +188,13 @@
     /**
      * Listen for {@link PreciseDataConnectionState} on the data connection (cellular).
      *
-     * @see #onPreciseDataConnectionStateChanged
+     * <p>Requires permission {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
-     * @hide
+     * @see #onPreciseDataConnectionStateChanged
      */
-    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
-    @SystemApi
+    @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE))
     public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE            = 0x00001000;
 
     /**
@@ -624,29 +617,6 @@
         // default implementation empty
     }
 
-
-    /**
-     * The Over The Air Service Provisioning (OTASP) has changed on the registered subscription.
-     * Note, the registration subId comes from {@link TelephonyManager} object which registers
-     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
-     * If this TelephonyManager object was created with
-     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-     * subId. Otherwise, this callback applies to
-     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-     *
-     * Requires the READ_PHONE_STATE permission.
-     * @param otaspMode is integer <code>OTASP_UNKNOWN=1<code>
-     *   means the value is currently unknown and the system should wait until
-     *   <code>OTASP_NEEDED=2<code> or <code>OTASP_NOT_NEEDED=3<code> is received before
-     *   making the decision to perform OTASP or not.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public void onOtaspChanged(int otaspMode) {
-        // default implementation empty
-    }
-
     /**
      * Callback invoked when a observed cell info has changed or new cells have been added
      * or removed on the registered subscription.
@@ -719,8 +689,9 @@
     }
 
     /**
-     * Callback invoked when data connection state changes with precise information
-     * on the registered subscription.
+     * Callback providing update about the default/internet data connection on the registered
+     * subscription.
+     *
      * Note, the registration subId comes from {@link TelephonyManager} object which registers
      * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
      * If this TelephonyManager object was created with
@@ -728,12 +699,13 @@
      * subId. Otherwise, this callback applies to
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
-     * @param dataConnectionState {@link PreciseDataConnectionState}
+     * <p>Requires permission {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     * or the calling app has carrier privileges
+     * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
-     * @hide
+     * @param dataConnectionState {@link PreciseDataConnectionState}
      */
-    @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
-    @SystemApi
+    @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE))
     public void onPreciseDataConnectionStateChanged(
             @NonNull PreciseDataConnectionState dataConnectionState) {
         // default implementation empty
@@ -1042,11 +1014,21 @@
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
 
-            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
-                    () -> {
-                        psl.onDataConnectionStateChanged(state, networkType);
-                        psl.onDataConnectionStateChanged(state);
-                    }));
+            if (state == TelephonyManager.DATA_DISCONNECTING
+                    && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) {
+                Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                        () -> {
+                            psl.onDataConnectionStateChanged(
+                                    TelephonyManager.DATA_CONNECTED, networkType);
+                            psl.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED);
+                        }));
+            } else {
+                Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                        () -> {
+                            psl.onDataConnectionStateChanged(state, networkType);
+                            psl.onDataConnectionStateChanged(state);
+                        }));
+            }
         }
 
         public void onDataActivity(int direction) {
@@ -1065,14 +1047,6 @@
                     () -> mExecutor.execute(() -> psl.onSignalStrengthsChanged(signalStrength)));
         }
 
-        public void onOtaspChanged(int otaspMode) {
-            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
-            if (psl == null) return;
-
-            Binder.withCleanCallingIdentity(
-                    () -> mExecutor.execute(() -> psl.onOtaspChanged(otaspMode)));
-        }
-
         public void onCellInfoChanged(List<CellInfo> cellInfo) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 9d7b57b..f574160 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -21,17 +21,13 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.content.Context;
-import android.net.LinkProperties;
-import android.net.NetworkCapabilities;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.telephony.Annotation.ApnType;
 import android.telephony.Annotation.CallState;
 import android.telephony.Annotation.DataActivityType;
 import android.telephony.Annotation.DataFailureCause;
-import android.telephony.Annotation.DataState;
 import android.telephony.Annotation.NetworkType;
 import android.telephony.Annotation.PreciseCallStates;
 import android.telephony.Annotation.RadioPowerState;
@@ -357,27 +353,18 @@
      * @param subId for which data connection state changed.
      * @param slotIndex for which data connections state changed. Can be derived from subId except
      * when subId is invalid.
-     * @param state latest data connection state, e.g,
-     * @param isDataConnectivityPossible indicates if data is allowed
-     * @param apn the APN {@link ApnSetting#getApnName()} of this data connection.
-     * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN.
-     * @param linkProperties {@link LinkProperties} associated with this data connection.
-     * @param networkCapabilities {@link NetworkCapabilities} associated with this data connection.
-     * @param networkType associated with this data connection.
-     * @param roaming {@code true} indicates in roaming, {@false} otherwise.
-     * @see TelephonyManager#DATA_DISCONNECTED
-     * @see TelephonyManager#isDataConnectivityPossible()
+     * @param apnType the APN type that triggered this update
+     * @param preciseState the PreciseDataConnectionState
      *
+     * @see android.telephony.PreciseDataConnection
+     * @see TelephonyManager#DATA_DISCONNECTED
      * @hide
      */
-    public void notifyDataConnectionForSubscriber(int slotIndex, int subId, @DataState int state,
-        boolean isDataConnectivityPossible,
-        @ApnType String apn, String apnType, LinkProperties linkProperties,
-        NetworkCapabilities networkCapabilities, int networkType, boolean roaming) {
+    public void notifyDataConnectionForSubscriber(int slotIndex, int subId,
+            String apnType, PreciseDataConnectionState preciseState) {
         try {
-            sRegistry.notifyDataConnectionForSubscriber(slotIndex, subId, state,
-                isDataConnectivityPossible,
-                apn, apnType, linkProperties, networkCapabilities, networkType, roaming);
+            sRegistry.notifyDataConnectionForSubscriber(
+                    slotIndex, subId, apnType, preciseState);
         } catch (RemoteException ex) {
             // system process is dead
         }
@@ -600,22 +587,6 @@
     }
 
     /**
-     * Notify over the air sim provisioning(OTASP) mode changed on certain subscription.
-     *
-     * @param subId for which otasp mode changed.
-     * @param otaspMode latest mode for OTASP e.g, OTASP needed.
-     *
-     * @hide
-     */
-    public void notifyOtaspChanged(int subId, int otaspMode) {
-        try {
-            sRegistry.notifyOtaspChanged(subId, otaspMode);
-        } catch (RemoteException ex) {
-            // system process is dead
-        }
-    }
-
-    /**
      * Notify precise call state changed on certain subscription, including foreground, background
      * and ringcall states.
      *
@@ -662,25 +633,6 @@
     }
 
     /**
-     * Notify data connection failed on certain subscription.
-     *
-     * @param subId for which data connection failed.
-     * @param slotIndex for which data conenction faled. Can be derived from subId except when subId
-     * is invalid.
-     * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN. Note each data
-     * connection can support multiple anyTypes.
-     *
-     * @hide
-     */
-    public void notifyDataConnectionFailed(int subId, int slotIndex, String apnType) {
-        try {
-            sRegistry.notifyDataConnectionFailedForSubscriber(slotIndex, subId, apnType);
-        } catch (RemoteException ex) {
-            // system process is dead
-        }
-    }
-
-    /**
      * TODO change from bundle to CellLocation?
      * @hide
      */
diff --git a/core/java/android/util/LongArrayQueue.java b/core/java/android/util/LongArrayQueue.java
index d5f0484..5c701db 100644
--- a/core/java/android/util/LongArrayQueue.java
+++ b/core/java/android/util/LongArrayQueue.java
@@ -162,4 +162,24 @@
         final int index = (mTail == 0) ? mValues.length - 1 : mTail - 1;
         return mValues[index];
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        if (mSize <= 0) {
+            return "{}";
+        }
+
+        final StringBuilder buffer = new StringBuilder(mSize * 64);
+        buffer.append('{');
+        buffer.append(get(0));
+        for (int i = 1; i < mSize; i++) {
+            buffer.append(", ");
+            buffer.append(get(i));
+        }
+        buffer.append('}');
+        return buffer.toString();
+    }
 }
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 28eb79a..71ac578 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -16,13 +16,13 @@
 
 package android.view;
 
-import static android.view.DisplayEventReceiver.CONFIG_CHANGED_EVENT_SUPPRESS;
 import static android.view.DisplayEventReceiver.VSYNC_SOURCE_APP;
 import static android.view.DisplayEventReceiver.VSYNC_SOURCE_SURFACE_FLINGER;
 
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.FrameInfo;
+import android.graphics.Insets;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Build;
 import android.os.Handler;
@@ -219,9 +219,10 @@
     /**
      * Callback type: Animation callback to handle inset updates. This is separate from
      * {@link #CALLBACK_ANIMATION} as we need to "gather" all inset animation updates via
-     * {@link WindowInsetsAnimationController#changeInsets} for multiple ongoing animations but then
-     * update the whole view system with a single callback to {@link View#dispatchWindowInsetsAnimationProgress}
-     * that contains all the combined updated insets.
+     * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} for multiple
+     * ongoing animations but then update the whole view system with a single callback to
+     * {@link View#dispatchWindowInsetsAnimationProgress} that contains all the combined updated
+     * insets.
      * <p>
      * Both input and animation may change insets, so we need to run this after these callbacks, but
      * before traversals.
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 411508f..615dab0 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -66,6 +66,7 @@
     private static final String BOTTOM_MARKER = "@bottom";
     private static final String DP_MARKER = "@dp";
     private static final String RIGHT_MARKER = "@right";
+    private static final String LEFT_MARKER = "@left";
 
     /**
      * Category for overlays that allow emulating a display cutout on devices that don't have
@@ -647,6 +648,9 @@
         if (spec.endsWith(RIGHT_MARKER)) {
             offsetX = displayWidth;
             spec = spec.substring(0, spec.length() - RIGHT_MARKER.length()).trim();
+        } else if (spec.endsWith(LEFT_MARKER)) {
+            offsetX = 0;
+            spec = spec.substring(0, spec.length() - LEFT_MARKER.length()).trim();
         } else {
             offsetX = displayWidth / 2f;
         }
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 3d139cd..7ea4f30 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -21,7 +21,6 @@
 import static android.view.InsetsState.ISIDE_LEFT;
 import static android.view.InsetsState.ISIDE_RIGHT;
 import static android.view.InsetsState.ISIDE_TOP;
-import static android.view.InsetsState.toPublicType;
 
 import android.annotation.Nullable;
 import android.graphics.Insets;
@@ -34,7 +33,8 @@
 import android.view.InsetsState.InternalInsetsSide;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowInsetsAnimationListener.InsetsAnimation;
+import android.view.WindowInsetsAnimationCallback.AnimationBounds;
+import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -66,20 +66,21 @@
     private final @InsetsType int mTypes;
     private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier;
     private final InsetsController mController;
-    private final WindowInsetsAnimationListener.InsetsAnimation mAnimation;
+    private final WindowInsetsAnimationCallback.InsetsAnimation mAnimation;
     private final Rect mFrame;
     private Insets mCurrentInsets;
     private Insets mPendingInsets;
+    private float mPendingFraction;
     private boolean mFinished;
     private boolean mCancelled;
-    private int mFinishedShownTypes;
+    private boolean mShownOnFinish;
 
     @VisibleForTesting
     public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame,
             InsetsState state, WindowInsetsAnimationControlListener listener,
             @InsetsType int types,
             Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier,
-            InsetsController controller) {
+            InsetsController controller, long durationMs) {
         mConsumers = consumers;
         mListener = listener;
         mTypes = types;
@@ -97,9 +98,10 @@
         // TODO: Check for controllability first and wait for IME if needed.
         listener.onReady(this, types);
 
-        mAnimation = new WindowInsetsAnimationListener.InsetsAnimation(mTypes, mHiddenInsets,
-                mShownInsets);
-        mController.dispatchAnimationStarted(mAnimation);
+        mAnimation = new WindowInsetsAnimationCallback.InsetsAnimation(mTypes,
+                InsetsController.INTERPOLATOR, durationMs);
+        mController.dispatchAnimationStarted(mAnimation,
+                new AnimationBounds(mHiddenInsets, mShownInsets));
     }
 
     @Override
@@ -123,7 +125,7 @@
     }
 
     @Override
-    public void changeInsets(Insets insets) {
+    public void setInsetsAndAlpha(Insets insets, float alpha, float fraction) {
         if (mFinished) {
             throw new IllegalStateException(
                     "Can't change insets on an animation that is finished.");
@@ -132,6 +134,7 @@
             throw new IllegalStateException(
                     "Can't change insets on an animation that is cancelled.");
         }
+        mPendingFraction = sanitize(fraction);
         mPendingInsets = sanitize(insets);
         mController.scheduleApplyChangeInsets();
     }
@@ -155,30 +158,35 @@
         SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
         applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
         mCurrentInsets = mPendingInsets;
+        mAnimation.setFraction(mPendingFraction);
         if (mFinished) {
-            mController.notifyFinished(this, mFinishedShownTypes);
+            mController.notifyFinished(this, mShownOnFinish);
         }
         return mFinished;
     }
 
     @Override
-    public void finish(int shownTypes) {
+    public void finish(boolean shown) {
         if (mCancelled) {
             return;
         }
         InsetsState state = new InsetsState(mController.getState());
         for (int i = mConsumers.size() - 1; i >= 0; i--) {
             InsetsSourceConsumer consumer = mConsumers.valueAt(i);
-            boolean visible = (shownTypes & toPublicType(consumer.getType())) != 0;
-            state.getSource(consumer.getType()).setVisible(visible);
+            state.getSource(consumer.getType()).setVisible(shown);
         }
         Insets insets = getInsetsFromState(state, mFrame, null /* typeSideMap */);
-        changeInsets(insets);
+        setInsetsAndAlpha(insets, 1f /* alpha */, shown ? 1f : 0f /* fraction */);
         mFinished = true;
-        mFinishedShownTypes = shownTypes;
+        mShownOnFinish = shown;
     }
 
+    @Override
     @VisibleForTesting
+    public float getCurrentFraction() {
+        return mAnimation.getFraction();
+    }
+
     public void onCancelled() {
         if (mFinished) {
             return;
@@ -191,6 +199,10 @@
         return mAnimation;
     }
 
+    WindowInsetsAnimationControlListener getListener() {
+        return mListener;
+    }
+
     private Insets calculateInsets(InsetsState state, Rect frame,
             SparseArray<InsetsSourceConsumer> consumers, boolean shown,
             @Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
@@ -210,9 +222,16 @@
     }
 
     private Insets sanitize(Insets insets) {
+        if (insets == null) {
+            insets = getCurrentInsets();
+        }
         return Insets.max(Insets.min(insets, mShownInsets), mHiddenInsets);
     }
 
+    private static float sanitize(float alpha) {
+        return alpha >= 1 ? 1 : (alpha <= 0 ? 0 : alpha);
+    }
+
     private void updateLeashesForSide(@InternalInsetsSide int side, int offset, int inset,
             ArrayList<SurfaceParams> surfaceParams, InsetsState state) {
         ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 43fec82..8870311 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -39,6 +39,8 @@
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type;
 import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowInsetsAnimationCallback.AnimationBounds;
+import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 
@@ -55,11 +57,12 @@
 
     private static final int ANIMATION_DURATION_SHOW_MS = 275;
     private static final int ANIMATION_DURATION_HIDE_MS = 340;
-    private static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
     private static final int DIRECTION_NONE = 0;
     private static final int DIRECTION_SHOW = 1;
     private static final int DIRECTION_HIDE = 2;
 
+    static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
     @IntDef ({DIRECTION_NONE, DIRECTION_SHOW, DIRECTION_HIDE})
     private @interface AnimationDirection{}
 
@@ -85,8 +88,75 @@
             return object.getCurrentInsets();
         }
         @Override
-        public void set(WindowInsetsAnimationController object, Insets value) {
-            object.changeInsets(value);
+        public void set(WindowInsetsAnimationController controller, Insets value) {
+            controller.setInsetsAndAlpha(
+                    value, 1f /* alpha */, (((DefaultAnimationControlListener)
+                            ((InsetsAnimationControlImpl) controller).getListener())
+                                    .getRawProgress()));
+        }
+    }
+
+    private class DefaultAnimationControlListener implements WindowInsetsAnimationControlListener {
+
+        private WindowInsetsAnimationController mController;
+        private ObjectAnimator mAnimator;
+        private boolean mShow;
+
+        DefaultAnimationControlListener(boolean show) {
+            mShow = show;
+        }
+
+        @Override
+        public void onReady(WindowInsetsAnimationController controller, int types) {
+            mController = controller;
+            if (mShow) {
+                showDirectly(types);
+            } else {
+                hideDirectly(types);
+            }
+            mAnimationDirection = mShow ? DIRECTION_SHOW : DIRECTION_HIDE;
+            mAnimator = ObjectAnimator.ofObject(
+                    controller,
+                    new InsetsProperty(),
+                    sEvaluator,
+                    mShow ? controller.getHiddenStateInsets() : controller.getShownStateInsets(),
+                    mShow ? controller.getShownStateInsets() : controller.getHiddenStateInsets()
+            );
+            mAnimator.setDuration(getDurationMs());
+            mAnimator.setInterpolator(INTERPOLATOR);
+            mAnimator.addListener(new AnimatorListenerAdapter() {
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    onAnimationFinish();
+                }
+            });
+            mAnimator.start();
+        }
+
+        @Override
+        public void onCancelled() {
+            // Animator can be null when it is cancelled before onReady() completes.
+            if (mAnimator != null) {
+                mAnimator.cancel();
+            }
+        }
+
+        private void onAnimationFinish() {
+            mAnimationDirection = DIRECTION_NONE;
+            mController.finish(mShow);
+        }
+
+        private float getRawProgress() {
+            float fraction = (float) mAnimator.getCurrentPlayTime() / mAnimator.getDuration();
+            return mShow ? fraction : 1 - fraction;
+        }
+
+        private long getDurationMs() {
+            if (mAnimator != null) {
+                return mAnimator.getDuration();
+            }
+            return mShow ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS;
         }
     }
 
@@ -278,24 +348,25 @@
     }
 
     @Override
-    public void controlWindowInsetsAnimation(@InsetsType int types,
+    public void controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
             WindowInsetsAnimationControlListener listener) {
-        controlWindowInsetsAnimation(types, listener, false /* fromIme */);
+        controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs);
     }
 
     private void controlWindowInsetsAnimation(@InsetsType int types,
-            WindowInsetsAnimationControlListener listener, boolean fromIme) {
+            WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs) {
         // If the frame of our window doesn't span the entire display, the control API makes very
         // little sense, as we don't deal with negative insets. So just cancel immediately.
         if (!mState.getDisplayFrame().equals(mFrame)) {
             listener.onCancelled();
             return;
         }
-        controlAnimationUnchecked(types, listener, mFrame, fromIme);
+        controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs);
     }
 
     private void controlAnimationUnchecked(@InsetsType int types,
-            WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme) {
+            WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
+            long durationMs) {
         if (types == 0) {
             // nothing to animate.
             return;
@@ -326,7 +397,7 @@
 
         final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
                 frame, mState, listener, typesReady,
-                () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this);
+                () -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this, durationMs);
         mAnimationControls.add(controller);
     }
 
@@ -397,10 +468,13 @@
     }
 
     @VisibleForTesting
-    public void notifyFinished(InsetsAnimationControlImpl controller, int shownTypes) {
+    public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) {
         mAnimationControls.remove(controller);
-        hideDirectly(controller.getTypes() & ~shownTypes);
-        showDirectly(controller.getTypes() & shownTypes);
+        if (shown) {
+            showDirectly(controller.getTypes());
+        } else {
+            hideDirectly(controller.getTypes());
+        }
     }
 
     void notifyControlRevoked(InsetsSourceConsumer consumer) {
@@ -510,58 +584,11 @@
             return;
         }
 
-        WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() {
-
-            private WindowInsetsAnimationController mController;
-            private ObjectAnimator mAnimator;
-
-            @Override
-            public void onReady(WindowInsetsAnimationController controller, int types) {
-                mController = controller;
-                if (show) {
-                    showDirectly(types);
-                } else {
-                    hideDirectly(types);
-                }
-                mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
-                mAnimator = ObjectAnimator.ofObject(
-                        controller,
-                        new InsetsProperty(),
-                        sEvaluator,
-                        show ? controller.getHiddenStateInsets() : controller.getShownStateInsets(),
-                        show ? controller.getShownStateInsets() : controller.getHiddenStateInsets()
-                );
-                mAnimator.setDuration(show
-                        ? ANIMATION_DURATION_SHOW_MS
-                        : ANIMATION_DURATION_HIDE_MS);
-                mAnimator.setInterpolator(INTERPOLATOR);
-                mAnimator.addListener(new AnimatorListenerAdapter() {
-
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        onAnimationFinish();
-                    }
-                });
-                mAnimator.start();
-            }
-
-            @Override
-            public void onCancelled() {
-                // Animator can be null when it is cancelled before onReady() completes.
-                if (mAnimator != null) {
-                    mAnimator.cancel();
-                }
-            }
-
-            private void onAnimationFinish() {
-                mAnimationDirection = DIRECTION_NONE;
-                mController.finish(show ? types : 0);
-            }
-        };
-
+        final DefaultAnimationControlListener listener = new DefaultAnimationControlListener(show);
         // Show/hide animations always need to be relative to the display frame, in order that shown
         // and hidden state insets are correct.
-        controlAnimationUnchecked(types, listener, mState.getDisplayFrame(), fromIme);
+        controlAnimationUnchecked(
+                types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs());
     }
 
     private void hideDirectly(@InsetsType int types) {
@@ -592,12 +619,12 @@
     }
 
     @VisibleForTesting
-    public void dispatchAnimationStarted(WindowInsetsAnimationListener.InsetsAnimation animation) {
-        mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation);
+    public void dispatchAnimationStarted(InsetsAnimation animation, AnimationBounds bounds) {
+        mViewRoot.mView.dispatchWindowInsetsAnimationStarted(animation, bounds);
     }
 
     @VisibleForTesting
-    public void dispatchAnimationFinished(WindowInsetsAnimationListener.InsetsAnimation animation) {
+    public void dispatchAnimationFinished(InsetsAnimation animation) {
         mViewRoot.mView.dispatchWindowInsetsAnimationFinished(animation);
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9d4f3878..2d0b954 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -109,7 +109,8 @@
 import android.view.AccessibilityIterators.TextSegmentIterator;
 import android.view.AccessibilityIterators.WordTextSegmentIterator;
 import android.view.ContextMenu.ContextMenuInfo;
-import android.view.WindowInsetsAnimationListener.InsetsAnimation;
+import android.view.WindowInsetsAnimationCallback.AnimationBounds;
+import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityEventSource;
 import android.view.accessibility.AccessibilityManager;
@@ -4626,7 +4627,7 @@
 
         private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners;
 
-        private WindowInsetsAnimationListener mWindowInsetsAnimationListener;
+        private WindowInsetsAnimationCallback mWindowInsetsAnimationCallback;
 
         /**
          * This lives here since it's only valid for interactive views.
@@ -11091,33 +11092,55 @@
     }
 
     /**
-     * Sets a {@link WindowInsetsAnimationListener} to be notified about animations of windows that
+     * Sets a {@link WindowInsetsAnimationCallback} to be notified about animations of windows that
      * cause insets.
      *
      * @param listener The listener to set.
-     * @hide pending unhide
      */
-    public void setWindowInsetsAnimationListener(WindowInsetsAnimationListener listener) {
-        getListenerInfo().mWindowInsetsAnimationListener = listener;
+    public void setWindowInsetsAnimationCallback(@Nullable WindowInsetsAnimationCallback listener) {
+        getListenerInfo().mWindowInsetsAnimationCallback = listener;
     }
 
-    void dispatchWindowInsetsAnimationStarted(InsetsAnimation animation) {
-        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
-            mListenerInfo.mWindowInsetsAnimationListener.onStarted(animation);
+    /**
+     * Dispatches {@link WindowInsetsAnimationCallback#onStarted(InsetsAnimation, AnimationBounds)}
+     * when Window Insets animation is started.
+     * @param animation current animation
+     * @param bounds the upper and lower {@link AnimationBounds} that provides range of
+     *  {@link InsetsAnimation}.
+     * @return the upper and lower {@link AnimationBounds}.
+     */
+    @NonNull
+    public AnimationBounds dispatchWindowInsetsAnimationStarted(
+            @NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
+        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
+            return mListenerInfo.mWindowInsetsAnimationCallback.onStarted(animation, bounds);
         }
+        return bounds;
     }
 
-    WindowInsets dispatchWindowInsetsAnimationProgress(WindowInsets insets) {
-        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
-            return mListenerInfo.mWindowInsetsAnimationListener.onProgress(insets);
+    /**
+     * Dispatches {@link WindowInsetsAnimationCallback#onProgress(WindowInsets)}
+     * when Window Insets animation makes progress.
+     * @param insets The current {@link WindowInsets}.
+     * @return current {@link WindowInsets}.
+     */
+    @NonNull
+    public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets) {
+        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
+            return mListenerInfo.mWindowInsetsAnimationCallback.onProgress(insets);
         } else {
             return insets;
         }
     }
 
-    void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) {
-        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) {
-            mListenerInfo.mWindowInsetsAnimationListener.onFinished(animation);
+    /**
+     * Dispatches {@link WindowInsetsAnimationCallback#onFinished(InsetsAnimation)}
+     * when Window Insets animation finishes.
+     * @param animation The current ongoing {@link InsetsAnimation}.
+     */
+    public void dispatchWindowInsetsAnimationFinished(@NonNull InsetsAnimation animation) {
+        if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationCallback != null) {
+            mListenerInfo.mWindowInsetsAnimationCallback.onFinished(animation);
         }
     }
 
@@ -11253,7 +11276,6 @@
      * @return The {@link WindowInsetsController} or {@code null} if the view isn't attached to a
      *         a window.
      * @see Window#getInsetsController()
-     * @hide pending unhide
      */
     public @Nullable WindowInsetsController getWindowInsetsController() {
         if (mAttachInfo != null) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 853a302..4334bb5 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -51,7 +51,8 @@
 import android.util.Pools.SynchronizedPool;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
-import android.view.WindowInsetsAnimationListener.InsetsAnimation;
+import android.view.WindowInsetsAnimationCallback.AnimationBounds;
+import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -7198,16 +7199,20 @@
     }
 
     @Override
-    void dispatchWindowInsetsAnimationStarted(InsetsAnimation animation) {
-        super.dispatchWindowInsetsAnimationStarted(animation);
+    @NonNull
+    public AnimationBounds dispatchWindowInsetsAnimationStarted(
+            @NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
+        super.dispatchWindowInsetsAnimationStarted(animation, bounds);
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
-            getChildAt(i).dispatchWindowInsetsAnimationStarted(animation);
+            getChildAt(i).dispatchWindowInsetsAnimationStarted(animation, bounds);
         }
+        return bounds;
     }
 
     @Override
-    WindowInsets dispatchWindowInsetsAnimationProgress(WindowInsets insets) {
+    @NonNull
+    public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets) {
         insets = super.dispatchWindowInsetsAnimationProgress(insets);
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
@@ -7217,7 +7222,7 @@
     }
 
     @Override
-    void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) {
+    public void dispatchWindowInsetsAnimationFinished(@NonNull InsetsAnimation animation) {
         super.dispatchWindowInsetsAnimationFinished(animation);
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index af1882b..ff31115 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2573,7 +2573,8 @@
     /**
      * @return The {@link WindowInsetsController} associated with this window
      * @see View#getWindowInsetsController()
-     * @hide pending unhide
      */
-    public abstract @NonNull WindowInsetsController getInsetsController();
+    public @Nullable WindowInsetsController getInsetsController() {
+        return null;
+    }
 }
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index b16a4ca..2404c84 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -177,7 +177,7 @@
      * @return The insets that include system bars indicated by {@code typeMask}, taken from
      *         {@code typeInsetsMap}.
      */
-    private static Insets getInsets(Insets[] typeInsetsMap, @InsetsType int typeMask) {
+    static Insets getInsets(Insets[] typeInsetsMap, @InsetsType int typeMask) {
         Insets result = null;
         for (int i = FIRST; i <= LAST; i = i << 1) {
             if ((typeMask & i) == 0) {
@@ -289,9 +289,8 @@
      *
      * @param typeMask Bit mask of {@link InsetsType}s to query the insets for.
      * @return The insets.
-     *
-     * @hide pending unhide
      */
+    @NonNull
     public Insets getInsets(@InsetsType int typeMask) {
         return getInsets(mTypeInsetsMap, typeMask);
     }
@@ -313,8 +312,8 @@
      *                                  insets are not available for this type as the height of the
      *                                  IME is dynamic depending on the {@link EditorInfo} of the
      *                                  currently focused view, as well as the UI state of the IME.
-     * @hide pending unhide
      */
+    @NonNull
     public Insets getMaxInsets(@InsetsType int typeMask) throws IllegalArgumentException {
         if ((typeMask & IME) != 0) {
             throw new IllegalArgumentException("Unable to query the maximum insets for IME");
@@ -329,7 +328,6 @@
      * @param typeMask Bit mask of {@link Type.InsetsType}s to query visibility status.
      * @return {@code true} if and only if all windows included in {@code typeMask} are currently
      *         visible on screen.
-     * @hide pending unhide
      */
     public boolean isVisible(@InsetsType int typeMask) {
         for (int i = FIRST; i <= LAST; i = i << 1) {
@@ -874,7 +872,7 @@
         return typeInsetsMap;
     }
 
-    private static Insets insetInsets(Insets insets, int left, int top, int right, int bottom) {
+    static Insets insetInsets(Insets insets, int left, int top, int right, int bottom) {
         int newLeft = Math.max(0, insets.left - left);
         int newTop = Math.max(0, insets.top - top);
         int newRight = Math.max(0, insets.right - right);
@@ -1015,7 +1013,6 @@
          * @param insets The insets to set.
          *
          * @return itself
-         * @hide pending unhide
          */
         @NonNull
         public Builder setInsets(@InsetsType int typeMask, @NonNull Insets insets) {
@@ -1046,7 +1043,6 @@
          *                                  the IME is dynamic depending on the {@link EditorInfo}
          *                                  of the currently focused view, as well as the UI
          *                                  state of the IME.
-         * @hide pending unhide
          */
         @NonNull
         public Builder setMaxInsets(@InsetsType int typeMask, @NonNull Insets insets)
@@ -1070,7 +1066,6 @@
          * @param visible Whether to mark the windows as visible or not.
          *
          * @return itself
-         * @hide pending unhide
          */
         @NonNull
         public Builder setVisible(@InsetsType int typeMask, boolean visible) {
@@ -1145,7 +1140,6 @@
 
     /**
      * Class that defines different types of sources causing window insets.
-     * @hide pending unhide
      */
     public static final class Type {
 
diff --git a/core/java/android/view/WindowInsetsAnimationCallback.java b/core/java/android/view/WindowInsetsAnimationCallback.java
new file mode 100644
index 0000000..8ae8520
--- /dev/null
+++ b/core/java/android/view/WindowInsetsAnimationCallback.java
@@ -0,0 +1,263 @@
+/*
+ * 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.view;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Insets;
+import android.view.WindowInsets.Type.InsetsType;
+import android.view.animation.Interpolator;
+
+/**
+ * Interface that allows the application to listen to animation events for windows that cause
+ * insets.
+ */
+public interface WindowInsetsAnimationCallback {
+
+    /**
+     * Called when an inset animation gets started.
+     * <p>
+     * Note that, like {@link #onProgress}, dispatch of the animation start event is hierarchical:
+     * It will starts at the root of the view hierarchy and then traverse it and invoke the callback
+     * of the specific {@link View} that is being traversed. The method my return a modified
+     * instance of the bounds by calling {@link AnimationBounds#inset} to indicate that a part of
+     * the insets have been used to offset or clip its children, and the children shouldn't worry
+     * about that part anymore.
+     *
+     * @param animation The animation that is about to start.
+     * @param bounds The bounds in which animation happens.
+     * @return The animation representing the part of the insets that should be dispatched to the
+     *         subtree of the hierarchy.
+     */
+    @NonNull
+    default AnimationBounds onStarted(
+            @NonNull InsetsAnimation animation, @NonNull AnimationBounds bounds) {
+        return bounds;
+    }
+
+    /**
+     * Called when the insets change as part of running an animation. Note that even if multiple
+     * animations for different types are running, there will only be one progress callback per
+     * frame. The {@code insets} passed as an argument represents the overall state and will include
+     * all types, regardless of whether they are animating or not.
+     * <p>
+     * Note that insets dispatch is hierarchical: It will start at the root of the view hierarchy,
+     * and then traverse it and invoke the callback of the specific {@link View} being traversed.
+     * The method may return a modified instance by calling
+     * {@link WindowInsets#inset(int, int, int, int)} to indicate that a part of the insets have
+     * been used to offset or clip its children, and the children shouldn't worry about that part
+     * anymore.
+     * TODO: Introduce a way to map (type -> InsetAnimation) so app developer can query animation
+     *  for a given type e.g. callback.getAnimation(type) OR controller.getAnimation(type).
+     *  Or on the controller directly?
+     * @param insets The current insets.
+     * @return The insets to dispatch to the subtree of the hierarchy.
+     */
+    @NonNull
+    WindowInsets onProgress(@NonNull WindowInsets insets);
+
+    /**
+     * Called when an inset animation has finished.
+     *
+     * @param animation The animation that has finished running. This will be the same instance as
+     *                  passed into {@link #onStarted}
+     */
+    default void onFinished(@NonNull InsetsAnimation animation) {
+    }
+
+    /**
+     * Class representing an animation of a set of windows that cause insets.
+     */
+    final class InsetsAnimation {
+
+        private final @InsetsType int mTypeMask;
+        private float mFraction;
+        @Nullable private final Interpolator mInterpolator;
+        private long mDurationMs;
+
+        public InsetsAnimation(
+                @InsetsType int typeMask, @Nullable Interpolator interpolator, long durationMs) {
+            mTypeMask = typeMask;
+            mInterpolator = interpolator;
+            mDurationMs = durationMs;
+        }
+
+        /**
+         * @return The bitmask of {@link WindowInsets.Type.InsetsType}s that are animating.
+         */
+        public @InsetsType int getTypeMask() {
+            return mTypeMask;
+        }
+
+        /**
+         * Returns the raw fractional progress of this animation between
+         * {@link AnimationBounds#getLowerBound()} and {@link AnimationBounds#getUpperBound()}. Note
+         * that this progress is the global progress of the animation, whereas
+         * {@link WindowInsetsAnimationCallback#onProgress} will only dispatch the insets that may
+         * be inset with {@link WindowInsets#inset} by parents of views in the hierarchy.
+         * Progress per insets animation is global for the entire animation. One animation animates
+         * all things together (in, out, ...). If they don't animate together, we'd have
+         * multiple animations.
+         *
+         * @return The current progress of this animation.
+         */
+        @FloatRange(from = 0f, to = 1f)
+        public float getFraction() {
+            return mFraction;
+        }
+
+        /**
+         * Returns the interpolated fractional progress of this animation between
+         * {@link AnimationBounds#getLowerBound()} and {@link AnimationBounds#getUpperBound()}. Note
+         * that this progress is the global progress of the animation, whereas
+         * {@link WindowInsetsAnimationCallback#onProgress} will only dispatch the insets that may
+         * be inset with {@link WindowInsets#inset} by parents of views in the hierarchy.
+         * Progress per insets animation is global for the entire animation. One animation animates
+         * all things together (in, out, ...). If they don't animate together, we'd have
+         * multiple animations.
+         * @see #getFraction() for raw fraction.
+         * @return The current interpolated progress of this animation. -1 if interpolator isn't
+         * specified.
+         */
+        public float getInterpolatedFraction() {
+            if (mInterpolator != null) {
+                return mInterpolator.getInterpolation(mFraction);
+            }
+            return -1;
+        }
+
+        @Nullable
+        public Interpolator getInterpolator() {
+            return mInterpolator;
+        }
+
+        /**
+         * @return duration of animation in {@link java.util.concurrent.TimeUnit#MILLISECONDS}.
+         */
+        public long getDurationMillis() {
+            return mDurationMs;
+        }
+
+        /**
+         * Set fraction of the progress if {@link WindowInsets.Type.InsetsType} animation is
+         * controlled by the app {@see #getCurrentFraction}.
+         * <p>Note: If app didn't create {@link InsetsAnimation}, it shouldn't set progress either.
+         * Progress would be set by system with the system-default animation.
+         * </p>
+         * @param fraction fractional progress between 0 and 1 where 0 represents hidden and
+         *                zero progress and 1 represent fully shown final state.
+         */
+        public void setFraction(@FloatRange(from = 0f, to = 1f) float fraction) {
+            mFraction = fraction;
+        }
+
+        /**
+         * Set duration of the animation if {@link WindowInsets.Type.InsetsType} animation is
+         * controlled by the app.
+         * <p>Note: If app didn't create {@link InsetsAnimation}, it shouldn't set duration either.
+         * Duration would be set by system with the system-default animation.
+         * </p>
+         * @param durationMs in {@link java.util.concurrent.TimeUnit#MILLISECONDS}
+         */
+        public void setDuration(long durationMs) {
+            mDurationMs = durationMs;
+        }
+    }
+
+    /**
+     * Class representing the range of an {@link InsetsAnimation}
+     */
+    final class AnimationBounds {
+        private final Insets mLowerBound;
+        private final Insets mUpperBound;
+
+        public AnimationBounds(@NonNull Insets lowerBound, @NonNull Insets upperBound) {
+            mLowerBound = lowerBound;
+            mUpperBound = upperBound;
+        }
+
+        /**
+         * Queries the lower inset bound of the animation. If the animation is about showing or
+         * hiding a window that cause insets, the lower bound is {@link Insets#NONE} and the upper
+         * bound is the same as {@link WindowInsets#getInsets(int)} for the fully shown state. This
+         * is the same as {@link WindowInsetsAnimationController#getHiddenStateInsets} and
+         * {@link WindowInsetsAnimationController#getShownStateInsets} in case the listener gets
+         * invoked because of an animation that originates from
+         * {@link WindowInsetsAnimationController}.
+         * <p>
+         * However, if the size of a window that causes insets is changing, these are the
+         * lower/upper bounds of that size animation.
+         * </p>
+         * There are no overlapping animations for a specific type, but there may be multiple
+         * animations running at the same time for different inset types.
+         *
+         * @see #getUpperBound()
+         * @see WindowInsetsAnimationController#getHiddenStateInsets
+         */
+        @NonNull
+        public Insets getLowerBound() {
+            return mLowerBound;
+        }
+
+        /**
+         * Queries the upper inset bound of the animation. If the animation is about showing or
+         * hiding a window that cause insets, the lower bound is {@link Insets#NONE}
+         * nd the upper bound is the same as {@link WindowInsets#getInsets(int)} for the fully
+         * shown state. This is the same as
+         * {@link WindowInsetsAnimationController#getHiddenStateInsets} and
+         * {@link WindowInsetsAnimationController#getShownStateInsets} in case the listener gets
+         * invoked because of an animation that originates from
+         * {@link WindowInsetsAnimationController}.
+         * <p>
+         * However, if the size of a window that causes insets is changing, these are the
+         * lower/upper bounds of that size animation.
+         * <p>
+         * There are no overlapping animations for a specific type, but there may be multiple
+         * animations running at the same time for different inset types.
+         *
+         * @see #getLowerBound()
+         * @see WindowInsetsAnimationController#getShownStateInsets
+         */
+        @NonNull
+        public Insets getUpperBound() {
+            return mUpperBound;
+        }
+
+        /**
+         * Insets both the lower and upper bound by the specified insets. This is to be used in
+         * {@link WindowInsetsAnimationCallback#onStarted} to indicate that a part of the insets has
+         * been used to offset or clip its children, and the children shouldn't worry about that
+         * part anymore.
+         *
+         * @param insets The amount to inset.
+         * @return A copy of this instance inset in the given directions.
+         * @see WindowInsets#inset
+         * @see WindowInsetsAnimationCallback#onStarted
+         */
+        @NonNull
+        public AnimationBounds inset(@NonNull Insets insets) {
+            return new AnimationBounds(
+                    // TODO: refactor so that WindowInsets.insetInsets() is in a more appropriate
+                    //  place eventually.
+                    WindowInsets.insetInsets(
+                            mLowerBound, insets.left, insets.top, insets.right, insets.bottom),
+                    WindowInsets.insetInsets(
+                            mUpperBound, insets.left, insets.top, insets.right, insets.bottom));
+        }
+    }
+}
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
index 33fb327..8a226c1 100644
--- a/core/java/android/view/WindowInsetsAnimationControlListener.java
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -22,19 +22,17 @@
 
 /**
  * Interface that informs the client about {@link WindowInsetsAnimationController} state changes.
- * @hide pending unhide
  */
 public interface WindowInsetsAnimationControlListener {
 
     /**
-     * Gets called as soon as the animation is ready to be controlled. This may be
-     * delayed when the IME needs to redraw because of an {@link EditorInfo} change, or when the
-     * window is starting up.
+     * Called when the animation is ready to be controlled. This may be delayed when the IME needs
+     * to redraw because of an {@link EditorInfo} change, or when the window is starting up.
      *
      * @param controller The controller to control the inset animation.
      * @param types The {@link InsetsType}s it was able to gain control over. Note that this may be
      *              different than the types passed into
-     *              {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window
+     *              {@link WindowInsetsController#controlInputMethodAnimation} in case the window
      *              wasn't able to gain the controls because it wasn't the IME target or not
      *              currently the window that's controlling the system bars.
      */
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index 5cbf3b8..5149103 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -16,83 +16,127 @@
 
 package android.view;
 
+import android.annotation.FloatRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.graphics.Insets;
 import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowInsetsAnimationListener.InsetsAnimation;
+import android.view.WindowInsetsAnimationCallback.AnimationBounds;
 
 /**
- * Interface to control a window inset animation frame-by-frame.
- * @hide pending unhide
+ * Controller for app-driven animation of system windows.
+ *  <p>
+ *  {@code WindowInsetsAnimationController} lets apps animate system windows such as
+ *  the {@link android.inputmethodservice.InputMethodService IME}. The animation is
+ *  synchronized, such that changes the system windows and the app's current frame
+ *  are rendered at the same time.
+ *  <p>
+ *  Control is obtained through {@link WindowInsetsController#controlInputMethodAnimation}.
  */
+@SuppressLint("NotClosable")
 public interface WindowInsetsAnimationController {
 
     /**
      * Retrieves the {@link Insets} when the windows this animation is controlling are fully hidden.
      * <p>
+     * Note that these insets are always relative to the window, which is the same as being relative
+     * to {@link View#getRootView}
+     * <p>
      * If there are any animation listeners registered, this value is the same as
-     * {@link InsetsAnimation#getLowerBound()} that will be passed into the callbacks.
+     * {@link AnimationBounds#getLowerBound()} that is being be passed into the root view of the
+     * hierarchy.
      *
      * @return Insets when the windows this animation is controlling are fully hidden.
      *
-     * @see InsetsAnimation#getLowerBound()
+     * @see AnimationBounds#getLowerBound()
      */
     @NonNull Insets getHiddenStateInsets();
 
     /**
      * Retrieves the {@link Insets} when the windows this animation is controlling are fully shown.
      * <p>
-     * In case the size of a window causing insets is changing in the middle of the animation, we
-     * execute that height change after this animation has finished.
+     * Note that these insets are always relative to the window, which is the same as being relative
+     * to {@link View#getRootView}
      * <p>
      * If there are any animation listeners registered, this value is the same as
-     * {@link InsetsAnimation#getUpperBound()} that will be passed into the callbacks.
+     * {@link AnimationBounds#getUpperBound()} that is being passed into the root view of hierarchy.
      *
      * @return Insets when the windows this animation is controlling are fully shown.
      *
-     * @see InsetsAnimation#getUpperBound()
+     * @see AnimationBounds#getUpperBound()
      */
     @NonNull Insets getShownStateInsets();
 
     /**
-     * @return The current insets on the window. These will follow any animation changes.
+     * Retrieves the current insets.
+     * <p>
+     * Note that these insets are always relative to the window, which is the same as
+     * being relative
+     * to {@link View#getRootView}
+     * @return The current insets on the currently showing frame. These insets will change as the
+     * animation progresses to reflect the current insets provided by the controlled window.
      */
     @NonNull Insets getCurrentInsets();
 
     /**
+     *  Returns the progress as previously set by {@code fraction} in {@link #setInsetsAndAlpha}
+     *
+     *  @return the progress of the animation, where {@code 0} is fully hidden and {@code 1} is
+     *  fully shown.
+     * <p>
+     *  Note: this value represents raw overall progress of the animation
+     *  i.e. the combined progress of insets and alpha.
+     *  <p>
+     */
+    @FloatRange(from = 0f, to = 1f)
+    float getCurrentFraction();
+
+    /**
      * @return The {@link InsetsType}s this object is currently controlling.
      */
     @InsetsType int getTypes();
 
     /**
-     * Modifies the insets by indirectly moving the windows around in the system that are causing
-     * window insets.
+     * Modifies the insets for the frame being drawn by indirectly moving the windows around in the
+     * system that are causing window insets.
      * <p>
-     * Note that this will <b>not</b> inform the view system of a full inset change via
+     * Note that these insets are always relative to the window, which is the same as being relative
+     * to {@link View#getRootView}
+     * <p>
+     * Also note that this will <b>not</b> inform the view system of a full inset change via
      * {@link View#dispatchApplyWindowInsets} in order to avoid a full layout pass during the
      * animation. If you'd like to animate views during a window inset animation, register a
-     * {@link WindowInsetsAnimationListener} by calling
-     * {@link View#setWindowInsetsAnimationListener(WindowInsetsAnimationListener)} that will be
-     * notified about any insets change via {@link WindowInsetsAnimationListener#onProgress} during
+     * {@link WindowInsetsAnimationCallback} by calling
+     * {@link View#setWindowInsetsAnimationCallback(WindowInsetsAnimationCallback)} that will be
+     * notified about any insets change via {@link WindowInsetsAnimationCallback#onProgress} during
      * the animation.
      * <p>
      * {@link View#dispatchApplyWindowInsets} will instead be called once the animation has
      * finished, i.e. once {@link #finish} has been called.
+     * Note: If there are no insets, alpha animation is still applied.
      *
      * @param insets The new insets to apply. Based on the requested insets, the system will
      *               calculate the positions of the windows in the system causing insets such that
      *               the resulting insets of that configuration will match the passed in parameter.
      *               Note that these insets are being clamped to the range from
-     *               {@link #getHiddenStateInsets} to {@link #getShownStateInsets}
+     *               {@link #getHiddenStateInsets} to {@link #getShownStateInsets}.
+     *               If you intend on changing alpha only, pass null or {@link #getCurrentInsets()}.
+     * @param alpha  The new alpha to apply to the inset side.
+     * @param fraction instantaneous animation progress. This value is dispatched to
+     *                 {@link WindowInsetsAnimationCallback}.
      *
-     * @see WindowInsetsAnimationListener
-     * @see View#setWindowInsetsAnimationListener(WindowInsetsAnimationListener)
+     * @see WindowInsetsAnimationCallback
+     * @see View#setWindowInsetsAnimationCallback(WindowInsetsAnimationCallback)
      */
-    void changeInsets(@NonNull Insets insets);
+    void setInsetsAndAlpha(@Nullable Insets insets, @FloatRange(from = 0f, to = 1f) float alpha,
+            @FloatRange(from = 0f, to = 1f) float fraction);
 
     /**
-     * @param shownTypes The list of windows causing insets that should remain shown after finishing
-     *                   the animation.
+     * Finishes the animation, and leaves the windows shown or hidden. After invoking
+     * {@link #finish(boolean)}, this instance is no longer valid.
+     * @param shown if {@code true}, the windows will be shown after finishing the
+     *              animation. Otherwise they will be hidden.
      */
-    void finish(@InsetsType int shownTypes);
+    void finish(boolean shown);
 }
diff --git a/core/java/android/view/WindowInsetsAnimationListener.java b/core/java/android/view/WindowInsetsAnimationListener.java
deleted file mode 100644
index f734b4b..0000000
--- a/core/java/android/view/WindowInsetsAnimationListener.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.graphics.Insets;
-
-/**
- * Interface that allows the application to listen to animation events for windows that cause
- * insets.
- * @hide pending unhide
- */
-public interface WindowInsetsAnimationListener {
-
-    /**
-     * Called when an inset animation gets started.
-     *
-     * @param animation The animation that is about to start.
-     */
-    void onStarted(InsetsAnimation animation);
-
-    /**
-     * Called when the insets change as part of running an animation. Note that even if multiple
-     * animations for different types are running, there will only be one progress callback per
-     * frame. The {@code insets} passed as an argument represents the overall state and will include
-     * all types, regardless of whether they are animating or not.
-     * <p>
-     * Note that insets dispatch is hierarchical: It will start at the root of the view hierarchy,
-     * and then traverse it and invoke the callback of the specific {@link View} being traversed.
-     * The callback may return a modified instance by calling {@link WindowInsets#inset(int, int, int, int)}
-     * to indicate that a part of the insets have been used to offset or clip its children, and the
-     * children shouldn't worry about that part anymore.
-     *
-     * @param insets The current insets.
-     * @return The insets to dispatch to the subtree of the hierarchy.
-     */
-    WindowInsets onProgress(WindowInsets insets);
-
-    /**
-     * Called when an inset animation has finished.
-     *
-     * @param animation The animation that has finished running.
-     */
-    void onFinished(InsetsAnimation animation);
-
-    /**
-     * Class representing an animation of a set of windows that cause insets.
-     */
-    class InsetsAnimation {
-
-        private final @WindowInsets.Type.InsetsType int mTypeMask;
-        private final Insets mLowerBound;
-        private final Insets mUpperBound;
-
-        /**
-         * @hide
-         */
-        InsetsAnimation(int typeMask, Insets lowerBound, Insets upperBound) {
-            mTypeMask = typeMask;
-            mLowerBound = lowerBound;
-            mUpperBound = upperBound;
-        }
-
-        /**
-         * @return The bitmask of {@link WindowInsets.Type.InsetsType}s that are animating.
-         */
-        public @WindowInsets.Type.InsetsType int getTypeMask() {
-            return mTypeMask;
-        }
-
-        /**
-         * Queries the lower inset bound of the animation. If the animation is about showing or
-         * hiding a window that cause insets, the lower bound is {@link Insets#NONE} and the upper
-         * bound is the same as {@link WindowInsets#getInsets(int)} for the fully shown state. This
-         * is the same as {@link WindowInsetsAnimationController#getHiddenStateInsets} and
-         * {@link WindowInsetsAnimationController#getShownStateInsets} in case the listener gets
-         * invoked because of an animation that originates from
-         * {@link WindowInsetsAnimationController}.
-         * <p>
-         * However, if the size of a window that causes insets is changing, these are the
-         * lower/upper bounds of that size animation.
-         * <p>
-         * There are no overlapping animations for a specific type, but there may be two animations
-         * running at the same time for different inset types.
-         *
-         * @see #getUpperBound()
-         * @see WindowInsetsAnimationController#getHiddenStateInsets
-         * TODO: It's a bit weird that these are global per window but onProgress is hierarchical.
-         * TODO: If multiple types are animating, querying the bound per type isn't possible. Should
-         * we:
-         * 1. Offer bounds by type here?
-         * 2. Restrict one animation to one single type only?
-         * Returning WindowInsets here isn't feasible in case of overlapping animations: We can't
-         * fill in the insets for the types from the other animation into the WindowInsets object
-         * as it's changing as well.
-         */
-        public Insets getLowerBound() {
-            return mLowerBound;
-        }
-
-        /**
-         * @see #getLowerBound()
-         * @see WindowInsetsAnimationController#getShownStateInsets
-         */
-        public Insets getUpperBound() {
-            return mUpperBound;
-        }
-    }
-}
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index a045a6a..6de56be 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -30,7 +30,6 @@
  * Interface to control windows that generate insets.
  *
  * TODO Needs more information and examples once the API is more baked.
- * @hide pending unhide
  */
 public interface WindowInsetsController {
 
@@ -64,7 +63,10 @@
      */
     int APPEARANCE_LIGHT_NAVIGATION_BARS = 1 << 4;
 
-    /** Determines the appearance of system bars. */
+    /**
+     * Determines the appearance of system bars.
+     * @hide
+     */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, value = {APPEARANCE_OPAQUE_STATUS_BARS, APPEARANCE_OPAQUE_NAVIGATION_BARS,
             APPEARANCE_LOW_PROFILE_BARS, APPEARANCE_LIGHT_STATUS_BARS,
@@ -75,33 +77,40 @@
     /**
      * The default option for {@link #setSystemBarsBehavior(int)}. System bars will be forcibly
      * shown on any user interaction on the corresponding display if navigation bars are hidden by
-     * {@link #hide(int)} or {@link WindowInsetsAnimationController#changeInsets(Insets)}.
+     * {@link #hide(int)} or
+     * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}.
+     * @hide
      */
     int BEHAVIOR_SHOW_BARS_BY_TOUCH = 0;
 
     /**
      * Option for {@link #setSystemBarsBehavior(int)}: Window would like to remain interactive when
      * hiding navigation bars by calling {@link #hide(int)} or
-     * {@link WindowInsetsAnimationController#changeInsets(Insets)}.
+     * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}.
      *
      * <p>When system bars are hidden in this mode, they can be revealed with system gestures, such
      * as swiping from the edge of the screen where the bar is hidden from.</p>
+     * @hide
      */
     int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1;
 
     /**
      * Option for {@link #setSystemBarsBehavior(int)}: Window would like to remain interactive when
      * hiding navigation bars by calling {@link #hide(int)} or
-     * {@link WindowInsetsAnimationController#changeInsets(Insets)}.
+     * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)}.
      *
      * <p>When system bars are hidden in this mode, they can be revealed temporarily with system
      * gestures, such as swiping from the edge of the screen where the bar is hidden from. These
      * transient system bars will overlay app’s content, may have some degree of transparency, and
      * will automatically hide after a short timeout.</p>
+     * @hide
      */
     int BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE = 2;
 
-    /** Determines the behavior of system bars when hiding them by calling {@link #hide}. */
+    /**
+     * Determines the behavior of system bars when hiding them by calling {@link #hide}.
+     * @hide
+     */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {BEHAVIOR_SHOW_BARS_BY_TOUCH, BEHAVIOR_SHOW_BARS_BY_SWIPE,
             BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE})
@@ -139,23 +148,27 @@
      * the position of the windows in the system causing insets directly.
      *
      * @param types The {@link InsetsType}s the application has requested to control.
+     * @param durationMillis duration of animation in
+     *                       {@link java.util.concurrent.TimeUnit#MILLISECONDS}
      * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
      *                 windows are ready to be controlled, among other callbacks.
      * @hide
      */
-    void controlWindowInsetsAnimation(@InsetsType int types,
+    void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
             @NonNull WindowInsetsAnimationControlListener listener);
 
     /**
      * Lets the application control the animation for showing the IME in a frame-by-frame manner by
      * modifying the position of the IME when it's causing insets.
      *
+     * @param durationMillis duration of the animation in
+     *                       {@link java.util.concurrent.TimeUnit#MILLISECONDS}
      * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
      *                 IME are ready to be controlled, among other callbacks.
      */
-    default void controlInputMethodAnimation(
+    default void controlInputMethodAnimation(long durationMillis,
             @NonNull WindowInsetsAnimationControlListener listener) {
-        controlWindowInsetsAnimation(ime(), listener);
+        controlWindowInsetsAnimation(ime(), durationMillis, listener);
     }
 
     /**
@@ -166,7 +179,7 @@
      * the event by observing {@link View#onApplyWindowInsets} and checking visibility with
      * {@link WindowInsets#isVisible}.
      *
-     * @see #controlInputMethodAnimation(WindowInsetsAnimationControlListener)
+     * @see #controlInputMethodAnimation(long, WindowInsetsAnimationControlListener)
      * @see #hideInputMethod()
      */
     default void showInputMethod() {
@@ -181,7 +194,7 @@
      * the event by observing {@link View#onApplyWindowInsets} and checking visibility with
      * {@link WindowInsets#isVisible}.
      *
-     * @see #controlInputMethodAnimation(WindowInsetsAnimationControlListener)
+     * @see #controlInputMethodAnimation(long, WindowInsetsAnimationControlListener)
      * @see #showInputMethod()
      */
     default void hideInputMethod() {
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index d44fbd7..3e26f63 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 
 /**
@@ -38,19 +39,19 @@
  * guide.</p>
  *
  * <p><strong>XML attributes</strong></p>
- * <p> 
- * See {@link android.R.styleable#CompoundButton CompoundButton Attributes}, 
- * {@link android.R.styleable#Button Button Attributes}, 
- * {@link android.R.styleable#TextView TextView Attributes}, 
+ * <p>
+ * See {@link android.R.styleable#CompoundButton CompoundButton Attributes},
+ * {@link android.R.styleable#Button Button Attributes},
+ * {@link android.R.styleable#TextView TextView Attributes},
  * {@link android.R.styleable#View View Attributes}
  * </p>
  */
 public class RadioButton extends CompoundButton {
-    
+
     public RadioButton(Context context) {
         this(context, null);
     }
-    
+
     public RadioButton(Context context, AttributeSet attrs) {
         this(context, attrs, com.android.internal.R.attr.radioButtonStyle);
     }
@@ -81,4 +82,20 @@
     public CharSequence getAccessibilityClassName() {
         return RadioButton.class.getName();
     }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        if (getParent() instanceof RadioGroup) {
+            RadioGroup radioGroup = (RadioGroup) getParent();
+            if (radioGroup.getOrientation() == LinearLayout.HORIZONTAL) {
+                info.setCollectionItemInfo(AccessibilityNodeInfo.CollectionItemInfo.obtain(0, 1,
+                        radioGroup.getIndexWithinVisibleButtons(this), 1, false, isChecked()));
+            } else {
+                info.setCollectionItemInfo(AccessibilityNodeInfo.CollectionItemInfo.obtain(
+                        radioGroup.getIndexWithinVisibleButtons(this), 1, 0, 1,
+                        false, isChecked()));
+            }
+        }
+    }
 }
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index c62c16c..90bc0a3 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IdRes;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -26,6 +27,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStructure;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 
@@ -93,6 +95,7 @@
         if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
             setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
         }
+        setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
 
         // retrieve selected radio button as requested by the user in the
         // XML layout file
@@ -475,4 +478,50 @@
         }
         return null;
     }
-}
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        if (this.getOrientation() == HORIZONTAL) {
+            info.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(1,
+                    getVisibleChildCount(), false,
+                    AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_SINGLE));
+        } else {
+            info.setCollectionInfo(
+                    AccessibilityNodeInfo.CollectionInfo.obtain(getVisibleChildCount(),
+                    1, false,
+                    AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_SINGLE));
+        }
+    }
+
+    private int getVisibleChildCount() {
+        int count = 0;
+        for (int i = 0; i < getChildCount(); i++) {
+            if (this.getChildAt(i) instanceof RadioButton) {
+                if (((RadioButton) this.getChildAt(i)).getVisibility() == VISIBLE) {
+                    count++;
+                }
+            }
+        }
+        return count;
+    }
+
+    int getIndexWithinVisibleButtons(@Nullable View child) {
+        if (!(child instanceof RadioButton)) {
+            return -1;
+        }
+        int index = 0;
+        for (int i = 0; i < getChildCount(); i++) {
+            if (this.getChildAt(i) instanceof RadioButton) {
+                RadioButton radioButton = (RadioButton) this.getChildAt(i);
+                if (radioButton == child) {
+                    return index;
+                }
+                if (radioButton.getVisibility() == VISIBLE) {
+                    index++;
+                }
+            }
+        }
+        return -1;
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index c571737..a6304b1 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -3473,18 +3473,10 @@
         return applyAsync(context, parent, executor, listener, null);
     }
 
-    private CancellationSignal startTaskOnExecutor(AsyncApplyTask task, Executor executor) {
-        CancellationSignal cancelSignal = new CancellationSignal();
-        cancelSignal.setOnCancelListener(task);
-
-        task.executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
-        return cancelSignal;
-    }
-
     /** @hide */
     public CancellationSignal applyAsync(Context context, ViewGroup parent,
             Executor executor, OnViewAppliedListener listener, OnClickHandler handler) {
-        return startTaskOnExecutor(getAsyncApplyTask(context, parent, listener, handler), executor);
+        return getAsyncApplyTask(context, parent, listener, handler).startTaskOnExecutor(executor);
     }
 
     private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
@@ -3495,6 +3487,7 @@
 
     private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
             implements CancellationSignal.OnCancelListener {
+        final CancellationSignal mCancelSignal = new CancellationSignal();
         final RemoteViews mRV;
         final ViewGroup mParent;
         final Context mContext;
@@ -3545,6 +3538,7 @@
 
         @Override
         protected void onPostExecute(ViewTree viewTree) {
+            mCancelSignal.setOnCancelListener(null);
             if (mError == null) {
                 if (mListener != null) {
                     mListener.onViewInflated(viewTree.mRoot);
@@ -3582,6 +3576,12 @@
         public void onCancel() {
             cancel(true);
         }
+
+        private CancellationSignal startTaskOnExecutor(Executor executor) {
+            mCancelSignal.setOnCancelListener(this);
+            executeOnExecutor(executor == null ? AsyncTask.THREAD_POOL_EXECUTOR : executor);
+            return mCancelSignal;
+        }
     }
 
     /**
@@ -3646,8 +3646,8 @@
             }
         }
 
-        return startTaskOnExecutor(new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
-                context, listener, handler, v), executor);
+        return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
+                context, listener, handler, v).startTaskOnExecutor(executor);
     }
 
     private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 8856f99..f361784 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -111,6 +111,7 @@
 import com.android.internal.app.ResolverListAdapter.ViewHolder;
 import com.android.internal.app.chooser.ChooserTargetInfo;
 import com.android.internal.app.chooser.DisplayResolveInfo;
+import com.android.internal.app.chooser.MultiDisplayResolveInfo;
 import com.android.internal.app.chooser.NotSelectableTargetInfo;
 import com.android.internal.app.chooser.SelectableTargetInfo;
 import com.android.internal.app.chooser.SelectableTargetInfo.SelectableTargetInfoCommunicator;
@@ -1352,17 +1353,31 @@
         return getIntent().getBooleanExtra(Intent.EXTRA_AUTO_LAUNCH_SINGLE_CHOICE, true);
     }
 
-    @Override
-    public void showTargetDetails(ResolveInfo ri) {
-        if (ri == null) {
+    void showTargetDetails(TargetInfo ti) {
+        if (ti == null) {
             return;
         }
-
-        ComponentName name = ri.activityInfo.getComponentName();
+        ComponentName name = ti.getResolveInfo().activityInfo.getComponentName();
         boolean pinned = mPinnedSharedPrefs.getBoolean(name.flattenToString(), false);
-        ResolverTargetActionsDialogFragment f =
-                new ResolverTargetActionsDialogFragment(ri.loadLabel(getPackageManager()),
-                        name, pinned);
+
+        ResolverTargetActionsDialogFragment f;
+
+        // For multiple targets, include info on all targets
+        if (ti instanceof MultiDisplayResolveInfo) {
+            MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) ti;
+            List<CharSequence> labels = new ArrayList<>();
+
+            for (TargetInfo innerInfo : mti.getTargets()) {
+                labels.add(innerInfo.getResolveInfo().loadLabel(getPackageManager()));
+            }
+            f = new ResolverTargetActionsDialogFragment(
+                    mti.getResolveInfo().loadLabel(getPackageManager()), name, mti.getTargets(),
+                    labels);
+        } else {
+            f = new ResolverTargetActionsDialogFragment(
+                    ti.getResolveInfo().loadLabel(getPackageManager()), name, pinned);
+        }
+
         f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
     }
 
@@ -1416,8 +1431,26 @@
         }
 
         final long selectionCost = System.currentTimeMillis() - mChooserShownTime;
+
+        // Stacked apps get a disambiguation first
+        if (targetInfo instanceof MultiDisplayResolveInfo) {
+            MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo;
+            CharSequence[] labels = new CharSequence[mti.getTargets().size()];
+            int i = 0;
+            for (TargetInfo ti : mti.getTargets()) {
+                labels[i++] = ti.getResolveInfo().loadLabel(getPackageManager());
+            }
+            ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment(
+                    targetInfo.getDisplayLabel(),
+                    ((MultiDisplayResolveInfo) targetInfo).getTargets(), labels);
+
+            f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
+            return;
+        }
+
         super.startSelected(which, always, filtered);
 
+
         if (currentListAdapter.getCount() > 0) {
             // Log the index of which type of target the user picked.
             // Lower values mean the ranking was better.
@@ -2363,7 +2396,7 @@
                 itemView.setOnLongClickListener(v -> {
                     showTargetDetails(
                             mChooserMultiProfilePagerAdapter.getActiveListAdapter()
-                                    .resolveInfoForPosition(mListPosition, /* filtered */ true));
+                                    .targetInfoForPosition(mListPosition, /* filtered */ true));
                     return true;
                 });
             }
@@ -2615,7 +2648,7 @@
                     @Override
                     public boolean onLongClick(View v) {
                         showTargetDetails(
-                                mChooserListAdapter.resolveInfoForPosition(
+                                mChooserListAdapter.targetInfoForPosition(
                                         holder.getItemIndex(column), true));
                         return true;
                     }
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 4eccf21..a8a676d 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -38,17 +38,22 @@
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
 import com.android.internal.app.chooser.ChooserTargetInfo;
 import com.android.internal.app.chooser.DisplayResolveInfo;
+import com.android.internal.app.chooser.MultiDisplayResolveInfo;
 import com.android.internal.app.chooser.SelectableTargetInfo;
 import com.android.internal.app.chooser.TargetInfo;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 public class ChooserListAdapter extends ResolverListAdapter {
     private static final String TAG = "ChooserListAdapter";
     private static final boolean DEBUG = false;
 
+    private boolean mEnableStackedApps = true;
+
     public static final int NO_POSITION = -1;
     public static final int TARGET_BAD = -1;
     public static final int TARGET_CALLER = 0;
@@ -218,7 +223,25 @@
 
     void updateAlphabeticalList() {
         mSortedList.clear();
-        mSortedList.addAll(mDisplayList);
+        if (mEnableStackedApps) {
+            // Consolidate multiple targets from same app.
+            Map<String, DisplayResolveInfo> consolidated = new HashMap<>();
+            for (DisplayResolveInfo info : mDisplayList) {
+                String packageName = info.getResolvedComponentName().getPackageName();
+                if (consolidated.get(packageName) != null) {
+                    // create consolidated target
+                    MultiDisplayResolveInfo multiDisplayResolveInfo =
+                            new MultiDisplayResolveInfo(packageName, info);
+                    multiDisplayResolveInfo.addTarget(consolidated.get(packageName));
+                    consolidated.put(packageName, multiDisplayResolveInfo);
+                } else {
+                    consolidated.put(packageName, info);
+                }
+            }
+            mSortedList.addAll(consolidated.values());
+        } else {
+            mSortedList.addAll(mDisplayList);
+        }
         Collections.sort(mSortedList, new ChooserActivity.AzInfoComparator(mContext));
     }
 
@@ -270,7 +293,7 @@
     }
 
     int getAlphaTargetCount() {
-        int standardCount = super.getCount();
+        int standardCount = mSortedList.size();
         return standardCount > mChooserListCommunicator.getMaxRankedTargets() ? standardCount : 0;
     }
 
diff --git a/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
new file mode 100644
index 0000000..ff6582d
--- /dev/null
+++ b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
@@ -0,0 +1,86 @@
+/*
+ * 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.app;
+
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.content.res.Configuration;
+import android.os.Bundle;
+
+import com.android.internal.app.chooser.DisplayResolveInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Shows individual actions for a "stacked" app target - such as an app with multiple posting
+ * streams represented in the Sharesheet.
+ */
+public class ChooserStackedAppDialogFragment extends DialogFragment
+        implements DialogInterface.OnClickListener {
+    private static final String TITLE_KEY = "title";
+    private static final String PINNED_KEY = "pinned";
+
+    private List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
+    private CharSequence[] mLabels;
+
+    public ChooserStackedAppDialogFragment() {
+    }
+
+    public ChooserStackedAppDialogFragment(CharSequence title) {
+        Bundle args = new Bundle();
+        args.putCharSequence(TITLE_KEY, title);
+        setArguments(args);
+    }
+
+    public ChooserStackedAppDialogFragment(CharSequence title,
+            List<DisplayResolveInfo> targets, CharSequence[] labels) {
+        Bundle args = new Bundle();
+        args.putCharSequence(TITLE_KEY, title);
+        mTargetInfos = targets;
+        mLabels = labels;
+        setArguments(args);
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final Bundle args = getArguments();
+        return new Builder(getContext())
+                .setCancelable(true)
+                .setItems(mLabels, this)
+                .setTitle(args.getCharSequence(TITLE_KEY))
+                .create();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        final Bundle args = getArguments();
+        mTargetInfos.get(which).start(getActivity(), null);
+        dismiss();
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        // Dismiss on config changed (eg: rotation)
+        // TODO: Maintain state on config change
+        super.onConfigurationChanged(newConfig);
+        dismiss();
+    }
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index cb7f2e4..8dc3a07 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1160,7 +1160,7 @@
         return !target.isSuspended();
     }
 
-    public void showTargetDetails(ResolveInfo ri) {
+    void showTargetDetails(ResolveInfo ri) {
         Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
diff --git a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
index df91c4a..bdbe210 100644
--- a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
+++ b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
@@ -24,14 +24,19 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.res.Configuration;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Settings;
 
 import com.android.internal.R;
+import com.android.internal.app.chooser.DisplayResolveInfo;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * Shows a dialog with actions to take on a chooser target
+ * Shows a dialog with actions to take on a chooser target.
  */
 public class ResolverTargetActionsDialogFragment extends DialogFragment
         implements DialogInterface.OnClickListener {
@@ -43,6 +48,10 @@
     private static final int TOGGLE_PIN_INDEX = 0;
     private static final int APP_INFO_INDEX = 1;
 
+    private List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
+    private List<CharSequence> mLabels = new ArrayList<>();
+    private boolean[] mPinned;
+
     public ResolverTargetActionsDialogFragment() {
     }
 
@@ -55,15 +64,43 @@
         setArguments(args);
     }
 
+    public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name,
+            List<DisplayResolveInfo> targets, List<CharSequence> labels) {
+        Bundle args = new Bundle();
+        args.putCharSequence(TITLE_KEY, title);
+        args.putParcelable(NAME_KEY, name);
+        mTargetInfos = targets;
+        mLabels = labels;
+        setArguments(args);
+    }
+
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         final Bundle args = getArguments();
         final int itemRes = args.getBoolean(PINNED_KEY, false)
                 ? R.array.resolver_target_actions_unpin
                 : R.array.resolver_target_actions_pin;
+        String[] defaultActions = getResources().getStringArray(itemRes);
+        CharSequence[] items;
+
+        if (mTargetInfos == null || mTargetInfos.size() < 2) {
+            items = defaultActions;
+        } else {
+            // Pin item for each sub-item
+            items = new CharSequence[mTargetInfos.size() + 1];
+            for (int i = 0; i < mTargetInfos.size(); i++) {
+                items[i] = mTargetInfos.get(i).isPinned()
+                         ? getResources().getString(R.string.unpin_specific_target, mLabels.get(i))
+                         : getResources().getString(R.string.pin_specific_target, mLabels.get(i));
+            }
+            // "App info"
+            items[mTargetInfos.size()] = defaultActions[1];
+        }
+
+
         return new Builder(getContext())
                 .setCancelable(true)
-                .setItems(itemRes, this)
+                .setItems(items, this)
                 .setTitle(args.getCharSequence(TITLE_KEY))
                 .create();
     }
@@ -72,27 +109,41 @@
     public void onClick(DialogInterface dialog, int which) {
         final Bundle args = getArguments();
         ComponentName name = args.getParcelable(NAME_KEY);
-        switch (which) {
-            case TOGGLE_PIN_INDEX:
-                SharedPreferences sp = ChooserActivity.getPinnedSharedPrefs(getContext());
-                final String key = name.flattenToString();
-                boolean currentVal = sp.getBoolean(name.flattenToString(), false);
-                if (currentVal) {
-                    sp.edit().remove(key).apply();
-                } else {
-                    sp.edit().putBoolean(key, true).apply();
-                }
-
-                // Force the chooser to requery and resort things
-                getActivity().recreate();
-                break;
-            case APP_INFO_INDEX:
-                Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
-                        .setData(Uri.fromParts("package", name.getPackageName(), null))
-                        .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
-                startActivity(in);
-                break;
+        if (which == 0 || (mTargetInfos.size() > 0 && which < mTargetInfos.size())) {
+            if (mTargetInfos == null || mTargetInfos.size() == 0) {
+                pinComponent(name);
+            } else {
+                pinComponent(mTargetInfos.get(which).getResolvedComponentName());
+            }
+            // Force the chooser to requery and resort things
+            getActivity().recreate();
+        } else {
+            // Last item in dialog is App Info
+            Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+                    .setData(Uri.fromParts("package", name.getPackageName(), null))
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+            startActivity(in);
         }
         dismiss();
     }
+
+    private void pinComponent(ComponentName name) {
+        SharedPreferences sp = ChooserActivity.getPinnedSharedPrefs(getContext());
+        final String key = name.flattenToString();
+        boolean currentVal = sp.getBoolean(name.flattenToString(), false);
+        if (currentVal) {
+            sp.edit().remove(key).apply();
+        } else {
+            sp.edit().putBoolean(key, true).apply();
+        }
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        // Dismiss on config changed (eg: rotation)
+        // TODO: Maintain state on config change
+        super.onConfigurationChanged(newConfig);
+        dismiss();
+    }
+
 }
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index f92637c..86a9af3 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -91,6 +91,16 @@
         mResolveInfoPresentationGetter = resolveInfoPresentationGetter;
     }
 
+    DisplayResolveInfo(DisplayResolveInfo other) {
+        mSourceIntents.addAll(other.getAllSourceIntents());
+        mResolveInfo = other.mResolveInfo;
+        mDisplayLabel = other.mDisplayLabel;
+        mDisplayIcon = other.mDisplayIcon;
+        mExtendedInfo = other.mExtendedInfo;
+        mResolvedIntent = other.mResolvedIntent;
+        mResolveInfoPresentationGetter = other.mResolveInfoPresentationGetter;
+    }
+
     public ResolveInfo getResolveInfo() {
         return mResolveInfo;
     }
@@ -189,4 +199,5 @@
     public void setPinned(boolean pinned) {
         mPinned = pinned;
     }
+
 }
diff --git a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
new file mode 100644
index 0000000..4c52411
--- /dev/null
+++ b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
@@ -0,0 +1,55 @@
+/*
+ * 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.app.chooser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a "stack" of chooser targets for various activities within the same component.
+ */
+public class MultiDisplayResolveInfo extends DisplayResolveInfo {
+
+    List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
+    String mPackageName;
+    // We'll use this DRI for basic presentation info - eg icon, name.
+    final DisplayResolveInfo mBaseInfo;
+
+    /**
+     * @param firstInfo A representative DRI to use for the main icon, title, etc for this Info.
+     */
+    public MultiDisplayResolveInfo(String packageName, DisplayResolveInfo firstInfo) {
+        super(firstInfo);
+        mBaseInfo = firstInfo;
+        mTargetInfos.add(firstInfo);
+    }
+
+    /**
+     * Add another DisplayResolveInfo to the list included for this target.
+     */
+    public void addTarget(DisplayResolveInfo target) {
+        mTargetInfos.add(target);
+    }
+
+    /**
+     * List of all DisplayResolveInfos included in this target.
+     */
+    public List<DisplayResolveInfo> getTargets() {
+        return mTargetInfos;
+    }
+
+}
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java
index d78bd73..b250578 100644
--- a/core/java/com/android/internal/infra/AndroidFuture.java
+++ b/core/java/com/android/internal/infra/AndroidFuture.java
@@ -531,6 +531,12 @@
                     try {
                         AndroidFuture.this.complete((T) resultContainer.get());
                     } catch (Throwable t) {
+                        // If resultContainer was completed exceptionally, get() wraps the
+                        // underlying exception in an ExecutionException. Unwrap it now to avoid
+                        // double-layering ExecutionExceptions.
+                        if (t instanceof ExecutionException && t.getCause() != null) {
+                            t = t.getCause();
+                        }
                         completeExceptionally(t);
                     }
                 }
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 01f5743..cb67309 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -43,7 +43,6 @@
     void onDataConnectionStateChanged(int state, int networkType);
     void onDataActivity(int direction);
     void onSignalStrengthsChanged(in SignalStrength signalStrength);
-    void onOtaspChanged(in int otaspMode);
     void onCellInfoChanged(in List<CellInfo> cellInfo);
     void onPreciseCallStateChanged(in PreciseCallState callState);
     void onPreciseDataConnectionStateChanged(in PreciseDataConnectionState dataConnectionState);
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index d8e8d67..f954679 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -24,6 +24,8 @@
 import android.telephony.CellInfo;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.PhoneCapability;
+import android.telephony.PhysicalChannelConfig;
+import android.telephony.PreciseDataConnectionState;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.emergency.EmergencyNumber;
@@ -60,16 +62,13 @@
     @UnsupportedAppUsage(maxTargetSdk = 28)
     void notifyDataActivity(int state);
     void notifyDataActivityForSubscriber(in int subId, int state);
-    void notifyDataConnectionForSubscriber(int phoneId, int subId, int state,
-            boolean isDataConnectivityPossible,
-            String apn, String apnType, in LinkProperties linkProperties,
-            in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
-    void notifyDataConnectionFailedForSubscriber(int phoneId, int subId, String apnType);
+    void notifyDataConnectionForSubscriber(
+            int phoneId, int subId, String apnType, in PreciseDataConnectionState preciseState);
+    @UnsupportedAppUsage
+    void notifyDataConnectionFailed(String apnType);
     @UnsupportedAppUsage(maxTargetSdk = 28)
     void notifyCellLocation(in Bundle cellLocation);
     void notifyCellLocationForSubscriber(in int subId, in Bundle cellLocation);
-    @UnsupportedAppUsage(maxTargetSdk = 28)
-    void notifyOtaspChanged(in int subId, in int otaspMode);
     @UnsupportedAppUsage
     void notifyCellInfo(in List<CellInfo> cellInfo);
     void notifyPreciseCallState(int phoneId, int subId, int ringingCallState,
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 4165f20..4dac542 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -308,6 +308,17 @@
     }
 
     /**
+     * @see #add(List, Object)
+     */
+    public static @NonNull <K, V> Map<K, V> add(@Nullable Map<K, V> map, K key, V value) {
+        if (map == null || map == Collections.emptyMap()) {
+            map = new ArrayMap<>();
+        }
+        map.put(key, value);
+        return map;
+    }
+
+    /**
      * Similar to {@link List#remove}, but with support for list values of {@code null} and
      * {@link Collections#emptyList}
      */
diff --git a/core/java/com/android/internal/util/ScreenRecordHelper.java b/core/java/com/android/internal/util/ScreenRecordHelper.java
index 64d0898..ec7ed4e 100644
--- a/core/java/com/android/internal/util/ScreenRecordHelper.java
+++ b/core/java/com/android/internal/util/ScreenRecordHelper.java
@@ -24,10 +24,6 @@
  * Helper class to initiate a screen recording
  */
 public class ScreenRecordHelper {
-    private static final String SYSUI_PACKAGE = "com.android.systemui";
-    private static final String SYSUI_SCREENRECORD_LAUNCHER =
-            "com.android.systemui.screenrecord.ScreenRecordDialog";
-
     private final Context mContext;
 
     /**
@@ -42,8 +38,9 @@
      * Show dialog of screen recording options to user.
      */
     public void launchRecordPrompt() {
-        final ComponentName launcherComponent = new ComponentName(SYSUI_PACKAGE,
-                SYSUI_SCREENRECORD_LAUNCHER);
+        final ComponentName launcherComponent = ComponentName.unflattenFromString(
+                mContext.getResources().getString(
+                        com.android.internal.R.string.config_screenRecorderComponent));
         final Intent intent = new Intent();
         intent.setComponent(launcherComponent);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index f6f187f..8cad5a0 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -20,12 +20,6 @@
 public class ScreenshotHelper {
     private static final String TAG = "ScreenshotHelper";
 
-    private static final String SYSUI_PACKAGE = "com.android.systemui";
-    private static final String SYSUI_SCREENSHOT_SERVICE =
-            "com.android.systemui.screenshot.TakeScreenshotService";
-    private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
-            "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
-
     // Time until we give up on the screenshot & show an error instead.
     private final int SCREENSHOT_TIMEOUT_MS = 10000;
 
@@ -94,8 +88,9 @@
             if (mScreenshotConnection != null) {
                 return;
             }
-            final ComponentName serviceComponent = new ComponentName(SYSUI_PACKAGE,
-                    SYSUI_SCREENSHOT_SERVICE);
+            final ComponentName serviceComponent = ComponentName.unflattenFromString(
+                    mContext.getResources().getString(
+                            com.android.internal.R.string.config_screenshotServiceComponent));
             final Intent serviceIntent = new Intent();
 
             final Runnable mScreenshotTimeout = new Runnable() {
@@ -181,8 +176,9 @@
      */
     private void notifyScreenshotError() {
         // If the service process is killed, then ask it to clean up after itself
-        final ComponentName errorComponent = new ComponentName(SYSUI_PACKAGE,
-                SYSUI_SCREENSHOT_ERROR_RECEIVER);
+        final ComponentName errorComponent = ComponentName.unflattenFromString(
+                mContext.getResources().getString(
+                        com.android.internal.R.string.config_screenshotErrorReceiverComponent));
         // Broadcast needs to have a valid action.  We'll just pick
         // a generic one, since the receiver here doesn't care.
         Intent errorIntent = new Intent(Intent.ACTION_USER_PRESENT);
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 7562bad..8a59c99 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -25,6 +25,7 @@
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.Process;
 import android.os.SystemProperties;
 import android.os.Trace;
@@ -228,7 +229,7 @@
      * Map of system pre-defined, uniquely named actors; keys are namespace,
      * value maps actor name to package name.
      */
-    private ArrayMap<String, ArrayMap<String, String>> mNamedActors = null;
+    private Map<String, Map<String, String>> mNamedActors = null;
 
     public static SystemConfig getInstance() {
         if (!isSystemProcess()) {
@@ -412,7 +413,7 @@
     }
 
     @NonNull
-    public Map<String, ? extends Map<String, String>> getNamedActors() {
+    public Map<String, Map<String, String>> getNamedActors() {
         return mNamedActors != null ? mNamedActors : Collections.emptyMap();
     }
 
@@ -498,6 +499,19 @@
                 Environment.getSystemExtDirectory(), "etc", "sysconfig"), ALLOW_ALL);
         readPermissions(Environment.buildPath(
                 Environment.getSystemExtDirectory(), "etc", "permissions"), ALLOW_ALL);
+
+        // Skip loading configuration from apex if it is not a system process.
+        if (!isSystemProcess()) {
+            return;
+        }
+        // Read configuration of libs from apex module.
+        // TODO: Use a solid way to filter apex module folders?
+        for (File f: FileUtils.listFilesOrEmpty(Environment.getApexDirectory())) {
+            if (f.isFile() || f.getPath().contains("@")) {
+                continue;
+            }
+            readPermissions(Environment.buildPath(f, "etc", "permissions"), ALLOW_LIBS);
+        }
     }
 
     @VisibleForTesting
@@ -1069,7 +1083,7 @@
                                 mNamedActors = new ArrayMap<>();
                             }
 
-                            ArrayMap<String, String> nameToPkgMap = mNamedActors.get(namespace);
+                            Map<String, String> nameToPkgMap = mNamedActors.get(namespace);
                             if (nameToPkgMap == null) {
                                 nameToPkgMap = new ArrayMap<>();
                                 mNamedActors.put(namespace, nameToPkgMap);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index b91d359..a2f6a62 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -177,7 +177,6 @@
                 "android_hardware_HardwareBuffer.cpp",
                 "android_hardware_SensorManager.cpp",
                 "android_hardware_SerialPort.cpp",
-                "android_hardware_SoundTrigger.cpp",
                 "android_hardware_UsbDevice.cpp",
                 "android_hardware_UsbDeviceConnection.cpp",
                 "android_hardware_UsbRequest.cpp",
@@ -259,7 +258,6 @@
                 "libpdfium",
                 "libimg_utils",
                 "libnetd_client",
-                "libsoundtrigger",
                 "libprocessgroup",
                 "libnativebridge_lazy",
                 "libnativeloader_lazy",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index b8fd3ad..f7a994f 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -80,7 +80,6 @@
 extern int register_android_hardware_HardwareBuffer(JNIEnv *env);
 extern int register_android_hardware_SensorManager(JNIEnv *env);
 extern int register_android_hardware_SerialPort(JNIEnv *env);
-extern int register_android_hardware_SoundTrigger(JNIEnv *env);
 extern int register_android_hardware_UsbDevice(JNIEnv *env);
 extern int register_android_hardware_UsbDeviceConnection(JNIEnv *env);
 extern int register_android_hardware_UsbRequest(JNIEnv *env);
@@ -1508,7 +1507,6 @@
     REG_JNI(register_android_hardware_HardwareBuffer),
     REG_JNI(register_android_hardware_SensorManager),
     REG_JNI(register_android_hardware_SerialPort),
-    REG_JNI(register_android_hardware_SoundTrigger),
     REG_JNI(register_android_hardware_UsbDevice),
     REG_JNI(register_android_hardware_UsbDeviceConnection),
     REG_JNI(register_android_hardware_UsbRequest),
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
deleted file mode 100644
index 4376b0b..0000000
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ /dev/null
@@ -1,1071 +0,0 @@
-/*
-**
-** Copyright 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.
-*/
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "SoundTrigger-JNI"
-#include <utils/Log.h>
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include "core_jni_helpers.h"
-#include <system/sound_trigger.h>
-#include <soundtrigger/SoundTriggerCallback.h>
-#include <soundtrigger/SoundTrigger.h>
-#include <utils/RefBase.h>
-#include <utils/Vector.h>
-#include <binder/IMemory.h>
-#include <binder/MemoryDealer.h>
-#include "android_media_AudioFormat.h"
-
-using namespace android;
-
-static jclass gArrayListClass;
-static struct {
-    jmethodID    add;
-} gArrayListMethods;
-
-static jclass gUUIDClass;
-static struct {
-    jmethodID    toString;
-} gUUIDMethods;
-
-static const char* const kUnsupportedOperationExceptionClassPathName =
-     "java/lang/UnsupportedOperationException";
-static jclass gUnsupportedOperationExceptionClass;
-static const char* const kIllegalArgumentExceptionClassPathName =
-     "java/lang/IllegalArgumentException";
-static jclass gIllegalArgumentExceptionClass;
-
-static const char* const kSoundTriggerClassPathName = "android/hardware/soundtrigger/SoundTrigger";
-static jclass gSoundTriggerClass;
-
-static const char* const kModuleClassPathName = "android/hardware/soundtrigger/SoundTriggerModule";
-static jclass gModuleClass;
-static struct {
-    jfieldID    mNativeContext;
-    jfieldID    mId;
-} gModuleFields;
-static jmethodID   gPostEventFromNative;
-
-static const char* const kModulePropertiesClassPathName =
-                                     "android/hardware/soundtrigger/SoundTrigger$ModuleProperties";
-static jclass gModulePropertiesClass;
-static jmethodID   gModulePropertiesCstor;
-
-static const char* const kSoundModelClassPathName =
-                                     "android/hardware/soundtrigger/SoundTrigger$SoundModel";
-static jclass gSoundModelClass;
-static struct {
-    jfieldID    uuid;
-    jfieldID    vendorUuid;
-    jfieldID    data;
-} gSoundModelFields;
-
-static const char* const kGenericSoundModelClassPathName =
-                                     "android/hardware/soundtrigger/SoundTrigger$GenericSoundModel";
-static jclass gGenericSoundModelClass;
-
-static const char* const kKeyphraseClassPathName =
-                                     "android/hardware/soundtrigger/SoundTrigger$Keyphrase";
-static jclass gKeyphraseClass;
-static struct {
-    jfieldID id;
-    jfieldID recognitionModes;
-    jfieldID locale;
-    jfieldID text;
-    jfieldID users;
-} gKeyphraseFields;
-
-static const char* const kKeyphraseSoundModelClassPathName =
-                                 "android/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel";
-static jclass gKeyphraseSoundModelClass;
-static struct {
-    jfieldID    keyphrases;
-} gKeyphraseSoundModelFields;
-
-static const char* const kModelParamRangeClassPathName =
-                                "android/hardware/soundtrigger/SoundTrigger$ModelParamRange";
-static jclass gModelParamRangeClass;
-static jmethodID gModelParamRangeCstor;
-
-static const char* const kRecognitionConfigClassPathName =
-                                     "android/hardware/soundtrigger/SoundTrigger$RecognitionConfig";
-static jclass gRecognitionConfigClass;
-static struct {
-    jfieldID captureRequested;
-    jfieldID keyphrases;
-    jfieldID data;
-} gRecognitionConfigFields;
-
-static const char* const kRecognitionEventClassPathName =
-                                     "android/hardware/soundtrigger/SoundTrigger$RecognitionEvent";
-static jclass gRecognitionEventClass;
-static jmethodID   gRecognitionEventCstor;
-
-static const char* const kKeyphraseRecognitionEventClassPathName =
-                             "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionEvent";
-static jclass gKeyphraseRecognitionEventClass;
-static jmethodID   gKeyphraseRecognitionEventCstor;
-
-static const char* const kGenericRecognitionEventClassPathName =
-                             "android/hardware/soundtrigger/SoundTrigger$GenericRecognitionEvent";
-static jclass gGenericRecognitionEventClass;
-static jmethodID   gGenericRecognitionEventCstor;
-
-static const char* const kKeyphraseRecognitionExtraClassPathName =
-                             "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra";
-static jclass gKeyphraseRecognitionExtraClass;
-static jmethodID   gKeyphraseRecognitionExtraCstor;
-static struct {
-    jfieldID id;
-    jfieldID recognitionModes;
-    jfieldID coarseConfidenceLevel;
-    jfieldID confidenceLevels;
-} gKeyphraseRecognitionExtraFields;
-
-static const char* const kConfidenceLevelClassPathName =
-                             "android/hardware/soundtrigger/SoundTrigger$ConfidenceLevel";
-static jclass gConfidenceLevelClass;
-static jmethodID   gConfidenceLevelCstor;
-static struct {
-    jfieldID userId;
-    jfieldID confidenceLevel;
-} gConfidenceLevelFields;
-
-static const char* const kAudioFormatClassPathName =
-                             "android/media/AudioFormat";
-static jclass gAudioFormatClass;
-static jmethodID gAudioFormatCstor;
-
-static const char* const kSoundModelEventClassPathName =
-                                     "android/hardware/soundtrigger/SoundTrigger$SoundModelEvent";
-static jclass gSoundModelEventClass;
-static jmethodID   gSoundModelEventCstor;
-
-static Mutex gLock;
-
-enum {
-    SOUNDTRIGGER_STATUS_OK = 0,
-    SOUNDTRIGGER_STATUS_ERROR = INT_MIN,
-    SOUNDTRIGGER_PERMISSION_DENIED = -1,
-    SOUNDTRIGGER_STATUS_NO_INIT = -19,
-    SOUNDTRIGGER_STATUS_BAD_VALUE = -22,
-    SOUNDTRIGGER_STATUS_DEAD_OBJECT = -32,
-    SOUNDTRIGGER_INVALID_OPERATION = -38,
-};
-
-enum  {
-    SOUNDTRIGGER_EVENT_RECOGNITION = 1,
-    SOUNDTRIGGER_EVENT_SERVICE_DIED = 2,
-    SOUNDTRIGGER_EVENT_SOUNDMODEL = 3,
-    SOUNDTRIGGER_EVENT_SERVICE_STATE_CHANGE = 4,
-};
-
-static jint throwUnsupportedOperationException(JNIEnv *env)
-{
-    return env->ThrowNew(gUnsupportedOperationExceptionClass, nullptr);
-}
-
-static jint throwIllegalArgumentException(JNIEnv *env)
-{
-    return env->ThrowNew(gIllegalArgumentExceptionClass, nullptr);
-}
-
-// ----------------------------------------------------------------------------
-// ref-counted object for callbacks
-class JNISoundTriggerCallback: public SoundTriggerCallback
-{
-public:
-    JNISoundTriggerCallback(JNIEnv* env, jobject thiz, jobject weak_thiz);
-    ~JNISoundTriggerCallback();
-
-    virtual void onRecognitionEvent(struct sound_trigger_recognition_event *event);
-    virtual void onSoundModelEvent(struct sound_trigger_model_event *event);
-    virtual void onServiceStateChange(sound_trigger_service_state_t state);
-    virtual void onServiceDied();
-
-private:
-    jclass      mClass;     // Reference to SoundTrigger class
-    jobject     mObject;    // Weak ref to SoundTrigger Java object to call on
-};
-
-JNISoundTriggerCallback::JNISoundTriggerCallback(JNIEnv* env, jobject thiz, jobject weak_thiz)
-{
-
-    // Hold onto the SoundTriggerModule class for use in calling the static method
-    // that posts events to the application thread.
-    jclass clazz = env->GetObjectClass(thiz);
-    if (clazz == NULL) {
-        ALOGE("Can't find class %s", kModuleClassPathName);
-        return;
-    }
-    mClass = (jclass)env->NewGlobalRef(clazz);
-
-    // We use a weak reference so the SoundTriggerModule object can be garbage collected.
-    // The reference is only used as a proxy for callbacks.
-    mObject  = env->NewGlobalRef(weak_thiz);
-}
-
-JNISoundTriggerCallback::~JNISoundTriggerCallback()
-{
-    // remove global references
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
-    env->DeleteGlobalRef(mObject);
-    env->DeleteGlobalRef(mClass);
-}
-
-void JNISoundTriggerCallback::onRecognitionEvent(struct sound_trigger_recognition_event *event)
-{
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
-    jobject jEvent = NULL;
-    jbyteArray jData = NULL;
-
-    if (event->data_size) {
-        jData = env->NewByteArray(event->data_size);
-        jbyte *nData = env->GetByteArrayElements(jData, NULL);
-        memcpy(nData, (char *)event + event->data_offset, event->data_size);
-        env->ReleaseByteArrayElements(jData, nData, 0);
-    }
-
-    jobject jAudioFormat = NULL;
-    if (event->trigger_in_data || event->capture_available) {
-        jint channelMask = (jint)audio_channel_mask_get_bits(event->audio_config.channel_mask);
-        jint channelIndexMask = (jint)AUDIO_CHANNEL_NONE;
-
-        switch (audio_channel_mask_get_representation(event->audio_config.channel_mask)) {
-        case AUDIO_CHANNEL_REPRESENTATION_INDEX:
-            channelIndexMask = channelMask;
-            channelMask = (jint)AUDIO_CHANNEL_NONE;
-            break;
-        default:
-            break;
-        }
-        jAudioFormat = env->NewObject(gAudioFormatClass,
-                                    gAudioFormatCstor,
-                                    audioFormatFromNative(event->audio_config.format),
-                                    event->audio_config.sample_rate,
-                                    channelMask,
-                                    channelIndexMask);
-
-    }
-    if (event->type == SOUND_MODEL_TYPE_KEYPHRASE) {
-        struct sound_trigger_phrase_recognition_event *phraseEvent =
-                (struct sound_trigger_phrase_recognition_event *)event;
-
-        jobjectArray jExtras = env->NewObjectArray(phraseEvent->num_phrases,
-                                                  gKeyphraseRecognitionExtraClass, NULL);
-        if (jExtras == NULL) {
-            return;
-        }
-
-        for (size_t i = 0; i < phraseEvent->num_phrases; i++) {
-            jobjectArray jConfidenceLevels = env->NewObjectArray(
-                                                        phraseEvent->phrase_extras[i].num_levels,
-                                                        gConfidenceLevelClass, NULL);
-
-            if (jConfidenceLevels == NULL) {
-                return;
-            }
-            for (size_t j = 0; j < phraseEvent->phrase_extras[i].num_levels; j++) {
-                jobject jConfidenceLevel = env->NewObject(gConfidenceLevelClass,
-                                                  gConfidenceLevelCstor,
-                                                  phraseEvent->phrase_extras[i].levels[j].user_id,
-                                                  phraseEvent->phrase_extras[i].levels[j].level);
-                env->SetObjectArrayElement(jConfidenceLevels, j, jConfidenceLevel);
-                env->DeleteLocalRef(jConfidenceLevel);
-            }
-
-            jobject jNewExtra = env->NewObject(gKeyphraseRecognitionExtraClass,
-                                               gKeyphraseRecognitionExtraCstor,
-                                               phraseEvent->phrase_extras[i].id,
-                                               phraseEvent->phrase_extras[i].recognition_modes,
-                                               phraseEvent->phrase_extras[i].confidence_level,
-                                               jConfidenceLevels);
-
-            if (jNewExtra == NULL) {
-                return;
-            }
-            env->SetObjectArrayElement(jExtras, i, jNewExtra);
-            env->DeleteLocalRef(jNewExtra);
-            env->DeleteLocalRef(jConfidenceLevels);
-        }
-        jEvent = env->NewObject(gKeyphraseRecognitionEventClass, gKeyphraseRecognitionEventCstor,
-                                event->status, event->model, event->capture_available,
-                                event->capture_session, event->capture_delay_ms,
-                                event->capture_preamble_ms, event->trigger_in_data,
-                                jAudioFormat, jData, jExtras);
-        env->DeleteLocalRef(jExtras);
-    } else if (event->type == SOUND_MODEL_TYPE_GENERIC) {
-        jEvent = env->NewObject(gGenericRecognitionEventClass, gGenericRecognitionEventCstor,
-                                event->status, event->model, event->capture_available,
-                                event->capture_session, event->capture_delay_ms,
-                                event->capture_preamble_ms, event->trigger_in_data,
-                                jAudioFormat, jData);
-    } else {
-        jEvent = env->NewObject(gRecognitionEventClass, gRecognitionEventCstor,
-                                event->status, event->model, event->capture_available,
-                                event->capture_session, event->capture_delay_ms,
-                                event->capture_preamble_ms, event->trigger_in_data,
-                                jAudioFormat, jData);
-    }
-
-    if (jAudioFormat != NULL) {
-        env->DeleteLocalRef(jAudioFormat);
-    }
-    if (jData != NULL) {
-        env->DeleteLocalRef(jData);
-    }
-
-    env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
-                              SOUNDTRIGGER_EVENT_RECOGNITION, 0, 0, jEvent);
-
-    env->DeleteLocalRef(jEvent);
-    if (env->ExceptionCheck()) {
-        ALOGW("An exception occurred while notifying an event.");
-        env->ExceptionClear();
-    }
-}
-
-void JNISoundTriggerCallback::onSoundModelEvent(struct sound_trigger_model_event *event)
-{
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
-    jobject jEvent = NULL;
-    jbyteArray jData = NULL;
-
-    if (event->data_size) {
-        jData = env->NewByteArray(event->data_size);
-        jbyte *nData = env->GetByteArrayElements(jData, NULL);
-        memcpy(nData, (char *)event + event->data_offset, event->data_size);
-        env->ReleaseByteArrayElements(jData, nData, 0);
-    }
-
-    jEvent = env->NewObject(gSoundModelEventClass, gSoundModelEventCstor,
-                            event->status, event->model, jData);
-
-    env->DeleteLocalRef(jData);
-    env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
-                              SOUNDTRIGGER_EVENT_SOUNDMODEL, 0, 0, jEvent);
-    env->DeleteLocalRef(jEvent);
-    if (env->ExceptionCheck()) {
-        ALOGW("An exception occurred while notifying an event.");
-        env->ExceptionClear();
-    }
-}
-
-void JNISoundTriggerCallback::onServiceStateChange(sound_trigger_service_state_t state)
-{
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
-    env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
-                                        SOUNDTRIGGER_EVENT_SERVICE_STATE_CHANGE, state, 0, NULL);
-    if (env->ExceptionCheck()) {
-        ALOGW("An exception occurred while notifying an event.");
-        env->ExceptionClear();
-    }
-}
-
-void JNISoundTriggerCallback::onServiceDied()
-{
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
-
-    env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
-                              SOUNDTRIGGER_EVENT_SERVICE_DIED, 0, 0, NULL);
-    if (env->ExceptionCheck()) {
-        ALOGW("An exception occurred while notifying an event.");
-        env->ExceptionClear();
-    }
-}
-
-// ----------------------------------------------------------------------------
-
-static sp<SoundTrigger> getSoundTrigger(JNIEnv* env, jobject thiz)
-{
-    Mutex::Autolock l(gLock);
-    SoundTrigger* const st = (SoundTrigger*)env->GetLongField(thiz,
-                                                         gModuleFields.mNativeContext);
-    return sp<SoundTrigger>(st);
-}
-
-static sp<SoundTrigger> setSoundTrigger(JNIEnv* env, jobject thiz, const sp<SoundTrigger>& module)
-{
-    Mutex::Autolock l(gLock);
-    sp<SoundTrigger> old = (SoundTrigger*)env->GetLongField(thiz,
-                                                         gModuleFields.mNativeContext);
-    if (module.get()) {
-        module->incStrong((void*)setSoundTrigger);
-    }
-    if (old != 0) {
-        old->decStrong((void*)setSoundTrigger);
-    }
-    env->SetLongField(thiz, gModuleFields.mNativeContext, (jlong)module.get());
-    return old;
-}
-
-
-static jint
-android_hardware_SoundTrigger_listModules(JNIEnv *env, jobject clazz,
-                                          jstring opPackageName, jobject jModules)
-{
-    ALOGV("listModules");
-
-    if (jModules == NULL) {
-        ALOGE("listModules NULL AudioPatch ArrayList");
-        return SOUNDTRIGGER_STATUS_BAD_VALUE;
-    }
-    if (!env->IsInstanceOf(jModules, gArrayListClass)) {
-        ALOGE("listModules not an arraylist");
-        return SOUNDTRIGGER_STATUS_BAD_VALUE;
-    }
-
-    unsigned int numModules = 0;
-    struct sound_trigger_module_descriptor *nModules = NULL;
-
-    ScopedUtfChars opPackageNameStr(env, opPackageName);
-    const String16 opPackageNameString16 = String16(opPackageNameStr.c_str());
-
-    status_t status = SoundTrigger::listModules(opPackageNameString16, nModules, &numModules);
-    if (status != NO_ERROR || numModules == 0) {
-        return (jint)status;
-    }
-
-    nModules = (struct sound_trigger_module_descriptor *)
-                            calloc(numModules, sizeof(struct sound_trigger_module_descriptor));
-
-    status = SoundTrigger::listModules(opPackageNameString16, nModules, &numModules);
-    ALOGV("listModules SoundTrigger::listModules status %d numModules %d", status, numModules);
-
-    if (status != NO_ERROR) {
-        numModules = 0;
-    }
-
-    for (size_t i = 0; i < numModules; i++) {
-        char str[SOUND_TRIGGER_MAX_STRING_LEN];
-
-        jstring implementor = env->NewStringUTF(nModules[i].properties.implementor);
-        jstring description = env->NewStringUTF(nModules[i].properties.description);
-        SoundTrigger::guidToString(&nModules[i].properties.uuid,
-                                   str,
-                                   SOUND_TRIGGER_MAX_STRING_LEN);
-        jstring uuid = env->NewStringUTF(str);
-
-        ALOGV("listModules module %zu id %d description %s maxSoundModels %d",
-              i, nModules[i].handle, nModules[i].properties.description,
-              nModules[i].properties.max_sound_models);
-
-        jobject newModuleDesc = env->NewObject(gModulePropertiesClass, gModulePropertiesCstor,
-                                               nModules[i].handle,
-                                               implementor, description, uuid,
-                                               nModules[i].properties.version,
-                                               nModules[i].properties.max_sound_models,
-                                               nModules[i].properties.max_key_phrases,
-                                               nModules[i].properties.max_users,
-                                               nModules[i].properties.recognition_modes,
-                                               nModules[i].properties.capture_transition,
-                                               nModules[i].properties.max_buffer_ms,
-                                               nModules[i].properties.concurrent_capture,
-                                               nModules[i].properties.power_consumption_mw,
-                                               nModules[i].properties.trigger_in_event);
-
-        env->DeleteLocalRef(implementor);
-        env->DeleteLocalRef(description);
-        env->DeleteLocalRef(uuid);
-        if (newModuleDesc == NULL) {
-            status = SOUNDTRIGGER_STATUS_ERROR;
-            goto exit;
-        }
-        env->CallBooleanMethod(jModules, gArrayListMethods.add, newModuleDesc);
-    }
-
-exit:
-    free(nModules);
-    return (jint) status;
-}
-
-static void
-android_hardware_SoundTrigger_setup(JNIEnv *env, jobject thiz,
-                                    jstring opPackageName, jobject weak_this)
-{
-    ALOGV("setup");
-
-    ScopedUtfChars opPackageNameStr(env, opPackageName);
-    const String16 opPackageNameString16 = String16(opPackageNameStr.c_str());
-
-    sp<JNISoundTriggerCallback> callback = new JNISoundTriggerCallback(env, thiz, weak_this);
-
-    sound_trigger_module_handle_t handle =
-            (sound_trigger_module_handle_t)env->GetIntField(thiz, gModuleFields.mId);
-
-    sp<SoundTrigger> module = SoundTrigger::attach(opPackageNameString16, handle, callback);
-    if (module == 0) {
-        return;
-    }
-
-    setSoundTrigger(env, thiz, module);
-}
-
-static void
-android_hardware_SoundTrigger_detach(JNIEnv *env, jobject thiz)
-{
-    ALOGV("detach");
-    sp<SoundTrigger> module = setSoundTrigger(env, thiz, 0);
-    ALOGV("detach module %p", module.get());
-    if (module != 0) {
-        ALOGV("detach module->detach()");
-        module->detach();
-    }
-}
-
-static void
-android_hardware_SoundTrigger_finalize(JNIEnv *env, jobject thiz)
-{
-    ALOGV("finalize");
-    sp<SoundTrigger> module = getSoundTrigger(env, thiz);
-    if (module != 0) {
-        ALOGW("SoundTrigger finalized without being detached");
-    }
-    android_hardware_SoundTrigger_detach(env, thiz);
-}
-
-static jint
-android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz,
-                                             jobject jSoundModel, jintArray jHandle)
-{
-    jint status = SOUNDTRIGGER_STATUS_OK;
-    jbyte *nData = NULL;
-    struct sound_trigger_sound_model *nSoundModel;
-    jbyteArray jData;
-    sp<MemoryDealer> memoryDealer;
-    sp<IMemory> memory;
-    size_t size;
-    sound_model_handle_t handle = 0;
-    jobject jUuid;
-    jstring jUuidString;
-    const char *nUuidString;
-
-    ALOGV("loadSoundModel");
-    sp<SoundTrigger> module = getSoundTrigger(env, thiz);
-    if (module == NULL) {
-        return SOUNDTRIGGER_STATUS_ERROR;
-    }
-    if (jHandle == NULL) {
-        return SOUNDTRIGGER_STATUS_BAD_VALUE;
-    }
-    jsize jHandleLen = env->GetArrayLength(jHandle);
-    if (jHandleLen == 0) {
-        return SOUNDTRIGGER_STATUS_BAD_VALUE;
-    }
-    jint *nHandle = env->GetIntArrayElements(jHandle, NULL);
-    if (nHandle == NULL) {
-        return SOUNDTRIGGER_STATUS_ERROR;
-    }
-    if (!env->IsInstanceOf(jSoundModel, gSoundModelClass)) {
-        status = SOUNDTRIGGER_STATUS_BAD_VALUE;
-        goto exit;
-    }
-    size_t offset;
-    sound_trigger_sound_model_type_t type;
-    if (env->IsInstanceOf(jSoundModel, gKeyphraseSoundModelClass)) {
-        offset = sizeof(struct sound_trigger_phrase_sound_model);
-        type = SOUND_MODEL_TYPE_KEYPHRASE;
-    } else if (env->IsInstanceOf(jSoundModel, gGenericSoundModelClass)) {
-        offset = sizeof(struct sound_trigger_generic_sound_model);
-        type = SOUND_MODEL_TYPE_GENERIC;
-    } else {
-        offset = sizeof(struct sound_trigger_sound_model);
-        type = SOUND_MODEL_TYPE_UNKNOWN;
-    }
-
-    jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.uuid);
-    jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString);
-    nUuidString = env->GetStringUTFChars(jUuidString, NULL);
-    sound_trigger_uuid_t nUuid;
-    SoundTrigger::stringToGuid(nUuidString, &nUuid);
-    env->ReleaseStringUTFChars(jUuidString, nUuidString);
-    env->DeleteLocalRef(jUuidString);
-
-    sound_trigger_uuid_t nVendorUuid;
-    jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.vendorUuid);
-    if (jUuid != NULL) {
-        jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString);
-        nUuidString = env->GetStringUTFChars(jUuidString, NULL);
-        SoundTrigger::stringToGuid(nUuidString, &nVendorUuid);
-        env->ReleaseStringUTFChars(jUuidString, nUuidString);
-        env->DeleteLocalRef(jUuidString);
-    } else {
-        SoundTrigger::stringToGuid("00000000-0000-0000-0000-000000000000", &nVendorUuid);
-    }
-
-    jData = (jbyteArray)env->GetObjectField(jSoundModel, gSoundModelFields.data);
-    if (jData == NULL) {
-        status = SOUNDTRIGGER_STATUS_BAD_VALUE;
-        goto exit;
-    }
-    size = env->GetArrayLength(jData);
-
-    nData = env->GetByteArrayElements(jData, NULL);
-    if (jData == NULL) {
-        status = SOUNDTRIGGER_STATUS_ERROR;
-        goto exit;
-    }
-
-    memoryDealer = new MemoryDealer(offset + size, "SoundTrigge-JNI::LoadModel");
-    if (memoryDealer == 0) {
-        status = SOUNDTRIGGER_STATUS_ERROR;
-        goto exit;
-    }
-    memory = memoryDealer->allocate(offset + size);
-    if (memory == 0 || memory->unsecurePointer() == NULL) {
-        status = SOUNDTRIGGER_STATUS_ERROR;
-        goto exit;
-    }
-
-    nSoundModel = (struct sound_trigger_sound_model *)memory->unsecurePointer();
-
-    nSoundModel->type = type;
-    nSoundModel->uuid = nUuid;
-    nSoundModel->vendor_uuid = nVendorUuid;
-    nSoundModel->data_size = size;
-    nSoundModel->data_offset = offset;
-    memcpy((char *)nSoundModel + offset, nData, size);
-    if (type == SOUND_MODEL_TYPE_KEYPHRASE) {
-        struct sound_trigger_phrase_sound_model *phraseModel =
-                (struct sound_trigger_phrase_sound_model *)nSoundModel;
-
-        jobjectArray jPhrases =
-            (jobjectArray)env->GetObjectField(jSoundModel, gKeyphraseSoundModelFields.keyphrases);
-        if (jPhrases == NULL) {
-            status = SOUNDTRIGGER_STATUS_BAD_VALUE;
-            goto exit;
-        }
-
-        size_t numPhrases = env->GetArrayLength(jPhrases);
-        phraseModel->num_phrases = numPhrases;
-        ALOGV("loadSoundModel numPhrases %zu", numPhrases);
-        for (size_t i = 0; i < numPhrases; i++) {
-            jobject jPhrase = env->GetObjectArrayElement(jPhrases, i);
-            phraseModel->phrases[i].id =
-                                    env->GetIntField(jPhrase,gKeyphraseFields.id);
-            phraseModel->phrases[i].recognition_mode =
-                                    env->GetIntField(jPhrase,gKeyphraseFields.recognitionModes);
-
-            jintArray jUsers = (jintArray)env->GetObjectField(jPhrase, gKeyphraseFields.users);
-            phraseModel->phrases[i].num_users = env->GetArrayLength(jUsers);
-            jint *nUsers = env->GetIntArrayElements(jUsers, NULL);
-            memcpy(phraseModel->phrases[i].users,
-                   nUsers,
-                   phraseModel->phrases[i].num_users * sizeof(int));
-            env->ReleaseIntArrayElements(jUsers, nUsers, 0);
-            env->DeleteLocalRef(jUsers);
-
-            jstring jLocale = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.locale);
-            const char *nLocale = env->GetStringUTFChars(jLocale, NULL);
-            strncpy(phraseModel->phrases[i].locale,
-                    nLocale,
-                    SOUND_TRIGGER_MAX_LOCALE_LEN);
-            jstring jText = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.text);
-            const char *nText = env->GetStringUTFChars(jText, NULL);
-            strncpy(phraseModel->phrases[i].text,
-                    nText,
-                    SOUND_TRIGGER_MAX_STRING_LEN);
-
-            env->ReleaseStringUTFChars(jLocale, nLocale);
-            env->DeleteLocalRef(jLocale);
-            env->ReleaseStringUTFChars(jText, nText);
-            env->DeleteLocalRef(jText);
-            ALOGV("loadSoundModel phrases %zu text %s locale %s",
-                  i, phraseModel->phrases[i].text, phraseModel->phrases[i].locale);
-            env->DeleteLocalRef(jPhrase);
-        }
-        env->DeleteLocalRef(jPhrases);
-    } else if (type == SOUND_MODEL_TYPE_GENERIC) {
-        /* No initialization needed */
-    }
-    status = module->loadSoundModel(memory, &handle);
-    ALOGV("loadSoundModel status %d handle %d", status, handle);
-
-exit:
-    if (nHandle != NULL) {
-        nHandle[0] = (jint)handle;
-        env->ReleaseIntArrayElements(jHandle, nHandle, NULL);
-    }
-    if (nData != NULL) {
-        env->ReleaseByteArrayElements(jData, nData, NULL);
-    }
-    return status;
-}
-
-static jint
-android_hardware_SoundTrigger_unloadSoundModel(JNIEnv *env, jobject thiz,
-                                               jint jHandle)
-{
-    jint status = SOUNDTRIGGER_STATUS_OK;
-    ALOGV("unloadSoundModel");
-    sp<SoundTrigger> module = getSoundTrigger(env, thiz);
-    if (module == NULL) {
-        return SOUNDTRIGGER_STATUS_ERROR;
-    }
-    status = module->unloadSoundModel((sound_model_handle_t)jHandle);
-
-    return status;
-}
-
-static jint
-android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz,
-                                               jint jHandle, jobject jConfig)
-{
-    jint status = SOUNDTRIGGER_STATUS_OK;
-    ALOGV("startRecognition");
-    sp<SoundTrigger> module = getSoundTrigger(env, thiz);
-    if (module == NULL) {
-        return SOUNDTRIGGER_STATUS_ERROR;
-    }
-
-    if (!env->IsInstanceOf(jConfig, gRecognitionConfigClass)) {
-        return SOUNDTRIGGER_STATUS_BAD_VALUE;
-    }
-
-    jbyteArray jData = (jbyteArray)env->GetObjectField(jConfig, gRecognitionConfigFields.data);
-    jsize dataSize = 0;
-    jbyte *nData = NULL;
-    if (jData != NULL) {
-        dataSize = env->GetArrayLength(jData);
-        if (dataSize == 0) {
-            return SOUNDTRIGGER_STATUS_BAD_VALUE;
-        }
-        nData = env->GetByteArrayElements(jData, NULL);
-        if (nData == NULL) {
-            return SOUNDTRIGGER_STATUS_ERROR;
-        }
-    }
-
-    size_t totalSize = sizeof(struct sound_trigger_recognition_config) + dataSize;
-    sp<MemoryDealer> memoryDealer =
-            new MemoryDealer(totalSize, "SoundTrigge-JNI::StartRecognition");
-    if (memoryDealer == 0) {
-        return SOUNDTRIGGER_STATUS_ERROR;
-    }
-    sp<IMemory> memory = memoryDealer->allocate(totalSize);
-    if (memory == 0 || memory->unsecurePointer() == NULL) {
-        return SOUNDTRIGGER_STATUS_ERROR;
-    }
-    if (dataSize != 0) {
-        memcpy((char *)memory->unsecurePointer() + sizeof(struct sound_trigger_recognition_config),
-                nData,
-                dataSize);
-        env->ReleaseByteArrayElements(jData, nData, 0);
-    }
-    env->DeleteLocalRef(jData);
-    struct sound_trigger_recognition_config *config =
-                                    (struct sound_trigger_recognition_config *)memory->unsecurePointer();
-    config->data_size = dataSize;
-    config->data_offset = sizeof(struct sound_trigger_recognition_config);
-    config->capture_requested = env->GetBooleanField(jConfig,
-                                                 gRecognitionConfigFields.captureRequested);
-
-    config->num_phrases = 0;
-    jobjectArray jPhrases =
-        (jobjectArray)env->GetObjectField(jConfig, gRecognitionConfigFields.keyphrases);
-    if (jPhrases != NULL) {
-        config->num_phrases = env->GetArrayLength(jPhrases);
-    }
-    ALOGV("startRecognition num phrases %d", config->num_phrases);
-    for (size_t i = 0; i < config->num_phrases; i++) {
-        jobject jPhrase = env->GetObjectArrayElement(jPhrases, i);
-        config->phrases[i].id = env->GetIntField(jPhrase,
-                                                gKeyphraseRecognitionExtraFields.id);
-        config->phrases[i].recognition_modes = env->GetIntField(jPhrase,
-                                                gKeyphraseRecognitionExtraFields.recognitionModes);
-        config->phrases[i].confidence_level = env->GetIntField(jPhrase,
-                                            gKeyphraseRecognitionExtraFields.coarseConfidenceLevel);
-        config->phrases[i].num_levels = 0;
-        jobjectArray jConfidenceLevels = (jobjectArray)env->GetObjectField(jPhrase,
-                                                gKeyphraseRecognitionExtraFields.confidenceLevels);
-        if (jConfidenceLevels != NULL) {
-            config->phrases[i].num_levels = env->GetArrayLength(jConfidenceLevels);
-        }
-        ALOGV("startRecognition phrase %zu num_levels %d", i, config->phrases[i].num_levels);
-        for (size_t j = 0; j < config->phrases[i].num_levels; j++) {
-            jobject jConfidenceLevel = env->GetObjectArrayElement(jConfidenceLevels, j);
-            config->phrases[i].levels[j].user_id = env->GetIntField(jConfidenceLevel,
-                                                                    gConfidenceLevelFields.userId);
-            config->phrases[i].levels[j].level = env->GetIntField(jConfidenceLevel,
-                                                          gConfidenceLevelFields.confidenceLevel);
-            env->DeleteLocalRef(jConfidenceLevel);
-        }
-        ALOGV("startRecognition phrases %zu", i);
-        env->DeleteLocalRef(jConfidenceLevels);
-        env->DeleteLocalRef(jPhrase);
-    }
-    env->DeleteLocalRef(jPhrases);
-
-    status = module->startRecognition(jHandle, memory);
-    return status;
-}
-
-static jint
-android_hardware_SoundTrigger_stopRecognition(JNIEnv *env, jobject thiz,
-                                               jint jHandle)
-{
-    jint status = SOUNDTRIGGER_STATUS_OK;
-    ALOGV("stopRecognition");
-    sp<SoundTrigger> module = getSoundTrigger(env, thiz);
-    if (module == NULL) {
-        return SOUNDTRIGGER_STATUS_ERROR;
-    }
-    status = module->stopRecognition(jHandle);
-    return status;
-}
-
-static jint
-android_hardware_SoundTrigger_getModelState(JNIEnv *env, jobject thiz,
-                                            jint jHandle)
-{
-    jint status = SOUNDTRIGGER_STATUS_OK;
-    ALOGV("getModelState");
-    sp<SoundTrigger> module = getSoundTrigger(env, thiz);
-    if (module == NULL) {
-        return SOUNDTRIGGER_STATUS_ERROR;
-    }
-    status = module->getModelState(jHandle);
-    return status;
-}
-
-static jint
-android_hardware_SoundTrigger_setParameter(JNIEnv *env, jobject thiz,
-                                            jint jHandle, jint jModelParam, jint jValue)
-{
-    ALOGV("setParameter");
-    sp<SoundTrigger> module = getSoundTrigger(env, thiz);
-    if (module == NULL) {
-        return SOUNDTRIGGER_STATUS_NO_INIT;
-    }
-    return module->setParameter(jHandle, (sound_trigger_model_parameter_t) jModelParam, jValue);
-}
-
-static jint
-android_hardware_SoundTrigger_getParameter(JNIEnv *env, jobject thiz,
-                                            jint jHandle, jint jModelParam)
-{
-    ALOGV("getParameter");
-    sp<SoundTrigger> module = getSoundTrigger(env, thiz);
-    if (module == NULL) {
-        throwUnsupportedOperationException(env);
-        return -1;
-    }
-
-    jint nValue;
-    jint status = module->getParameter(jHandle,
-            (sound_trigger_model_parameter_t) jModelParam, &nValue);
-
-    switch (status) {
-        case 0:
-            return nValue;
-        case -EINVAL:
-            throwIllegalArgumentException(env);
-            break;
-        default:
-            throwUnsupportedOperationException(env);
-            break;
-    }
-
-    return -1;
-}
-
-static jobject
-android_hardware_SoundTrigger_queryParameter(JNIEnv *env, jobject thiz,
-                                            jint jHandle, jint jModelParam)
-{
-    ALOGV("queryParameter");
-    sp<SoundTrigger> module = getSoundTrigger(env, thiz);
-    if (module == nullptr) {
-        return nullptr;
-    }
-
-    sound_trigger_model_parameter_range_t nRange;
-    jint nValue = module->queryParameter(jHandle,
-            (sound_trigger_model_parameter_t) jModelParam, &nRange);
-
-    if (nValue != 0) {
-        ALOGE("failed to query parameter error code: %d", nValue);
-        return nullptr;
-    }
-
-    return env->NewObject(gModelParamRangeClass, gModelParamRangeCstor, nRange.start, nRange.end);
-}
-
-static const JNINativeMethod gMethods[] = {
-    {"listModules",
-        "(Ljava/lang/String;Ljava/util/ArrayList;)I",
-        (void *)android_hardware_SoundTrigger_listModules},
-};
-
-
-static const JNINativeMethod gModuleMethods[] = {
-    {"native_setup",
-        "(Ljava/lang/String;Ljava/lang/Object;)V",
-        (void *)android_hardware_SoundTrigger_setup},
-    {"native_finalize",
-        "()V",
-        (void *)android_hardware_SoundTrigger_finalize},
-    {"detach",
-        "()V",
-        (void *)android_hardware_SoundTrigger_detach},
-    {"loadSoundModel",
-        "(Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;[I)I",
-        (void *)android_hardware_SoundTrigger_loadSoundModel},
-    {"unloadSoundModel",
-        "(I)I",
-        (void *)android_hardware_SoundTrigger_unloadSoundModel},
-    {"startRecognition",
-        "(ILandroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I",
-        (void *)android_hardware_SoundTrigger_startRecognition},
-    {"stopRecognition",
-        "(I)I",
-        (void *)android_hardware_SoundTrigger_stopRecognition},
-    {"getModelState",
-        "(I)I",
-        (void *)android_hardware_SoundTrigger_getModelState},
-    {"setParameter",
-         "(III)I",
-         (void *)android_hardware_SoundTrigger_setParameter},
-    {"getParameter",
-         "(II)I",
-         (void *)android_hardware_SoundTrigger_getParameter},
-    {"queryParameter",
-         "(II)Landroid/hardware/soundtrigger/SoundTrigger$ModelParamRange;",
-         (void *)android_hardware_SoundTrigger_queryParameter}
-};
-
-int register_android_hardware_SoundTrigger(JNIEnv *env)
-{
-    jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
-    gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
-    gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
-
-    jclass uuidClass = FindClassOrDie(env, "java/util/UUID");
-    gUUIDClass = MakeGlobalRefOrDie(env, uuidClass);
-    gUUIDMethods.toString = GetMethodIDOrDie(env, uuidClass, "toString", "()Ljava/lang/String;");
-
-    jclass exUClass = FindClassOrDie(env, kUnsupportedOperationExceptionClassPathName);
-    gUnsupportedOperationExceptionClass = MakeGlobalRefOrDie(env, exUClass);
-
-    jclass exIClass = FindClassOrDie(env, kIllegalArgumentExceptionClassPathName);
-    gIllegalArgumentExceptionClass = MakeGlobalRefOrDie(env, exIClass);
-
-    jclass lClass = FindClassOrDie(env, kSoundTriggerClassPathName);
-    gSoundTriggerClass = MakeGlobalRefOrDie(env, lClass);
-
-    jclass moduleClass = FindClassOrDie(env, kModuleClassPathName);
-    gModuleClass = MakeGlobalRefOrDie(env, moduleClass);
-    gPostEventFromNative = GetStaticMethodIDOrDie(env, moduleClass, "postEventFromNative",
-                                                  "(Ljava/lang/Object;IIILjava/lang/Object;)V");
-    gModuleFields.mNativeContext = GetFieldIDOrDie(env, moduleClass, "mNativeContext", "J");
-    gModuleFields.mId = GetFieldIDOrDie(env, moduleClass, "mId", "I");
-
-    jclass modulePropertiesClass = FindClassOrDie(env, kModulePropertiesClassPathName);
-    gModulePropertiesClass = MakeGlobalRefOrDie(env, modulePropertiesClass);
-    gModulePropertiesCstor = GetMethodIDOrDie(env, modulePropertiesClass, "<init>",
-            "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIZIZIZ)V");
-
-    jclass soundModelClass = FindClassOrDie(env, kSoundModelClassPathName);
-    gSoundModelClass = MakeGlobalRefOrDie(env, soundModelClass);
-    gSoundModelFields.uuid = GetFieldIDOrDie(env, soundModelClass, "uuid", "Ljava/util/UUID;");
-    gSoundModelFields.vendorUuid = GetFieldIDOrDie(env, soundModelClass, "vendorUuid",
-                                                   "Ljava/util/UUID;");
-    gSoundModelFields.data = GetFieldIDOrDie(env, soundModelClass, "data", "[B");
-
-    jclass genericSoundModelClass = FindClassOrDie(env, kGenericSoundModelClassPathName);
-    gGenericSoundModelClass = MakeGlobalRefOrDie(env, genericSoundModelClass);
-
-    jclass keyphraseClass = FindClassOrDie(env, kKeyphraseClassPathName);
-    gKeyphraseClass = MakeGlobalRefOrDie(env, keyphraseClass);
-    gKeyphraseFields.id = GetFieldIDOrDie(env, keyphraseClass, "id", "I");
-    gKeyphraseFields.recognitionModes = GetFieldIDOrDie(env, keyphraseClass, "recognitionModes",
-                                                        "I");
-    gKeyphraseFields.locale = GetFieldIDOrDie(env, keyphraseClass, "locale", "Ljava/lang/String;");
-    gKeyphraseFields.text = GetFieldIDOrDie(env, keyphraseClass, "text", "Ljava/lang/String;");
-    gKeyphraseFields.users = GetFieldIDOrDie(env, keyphraseClass, "users", "[I");
-
-    jclass keyphraseSoundModelClass = FindClassOrDie(env, kKeyphraseSoundModelClassPathName);
-    gKeyphraseSoundModelClass = MakeGlobalRefOrDie(env, keyphraseSoundModelClass);
-    gKeyphraseSoundModelFields.keyphrases = GetFieldIDOrDie(env, keyphraseSoundModelClass,
-                                         "keyphrases",
-                                         "[Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;");
-
-    jclass modelParamRangeClass = FindClassOrDie(env, kModelParamRangeClassPathName);
-    gModelParamRangeClass = MakeGlobalRefOrDie(env, modelParamRangeClass);
-    gModelParamRangeCstor = GetMethodIDOrDie(env, modelParamRangeClass, "<init>", "(II)V");
-
-    jclass recognitionEventClass = FindClassOrDie(env, kRecognitionEventClassPathName);
-    gRecognitionEventClass = MakeGlobalRefOrDie(env, recognitionEventClass);
-    gRecognitionEventCstor = GetMethodIDOrDie(env, recognitionEventClass, "<init>",
-                                              "(IIZIIIZLandroid/media/AudioFormat;[B)V");
-
-    jclass keyphraseRecognitionEventClass = FindClassOrDie(env,
-                                                           kKeyphraseRecognitionEventClassPathName);
-    gKeyphraseRecognitionEventClass = MakeGlobalRefOrDie(env, keyphraseRecognitionEventClass);
-    gKeyphraseRecognitionEventCstor = GetMethodIDOrDie(env, keyphraseRecognitionEventClass, "<init>",
-              "(IIZIIIZLandroid/media/AudioFormat;[B[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V");
-
-    jclass genericRecognitionEventClass = FindClassOrDie(env,
-                                                           kGenericRecognitionEventClassPathName);
-    gGenericRecognitionEventClass = MakeGlobalRefOrDie(env, genericRecognitionEventClass);
-    gGenericRecognitionEventCstor = GetMethodIDOrDie(env, genericRecognitionEventClass, "<init>",
-                                              "(IIZIIIZLandroid/media/AudioFormat;[B)V");
-
-    jclass keyRecognitionConfigClass = FindClassOrDie(env, kRecognitionConfigClassPathName);
-    gRecognitionConfigClass = MakeGlobalRefOrDie(env, keyRecognitionConfigClass);
-    gRecognitionConfigFields.captureRequested = GetFieldIDOrDie(env, keyRecognitionConfigClass,
-                                                                "captureRequested", "Z");
-    gRecognitionConfigFields.keyphrases = GetFieldIDOrDie(env, keyRecognitionConfigClass,
-           "keyphrases", "[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;");
-    gRecognitionConfigFields.data = GetFieldIDOrDie(env, keyRecognitionConfigClass, "data", "[B");
-
-    jclass keyphraseRecognitionExtraClass = FindClassOrDie(env,
-                                                           kKeyphraseRecognitionExtraClassPathName);
-    gKeyphraseRecognitionExtraClass = MakeGlobalRefOrDie(env, keyphraseRecognitionExtraClass);
-    gKeyphraseRecognitionExtraCstor = GetMethodIDOrDie(env, keyphraseRecognitionExtraClass,
-            "<init>", "(III[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;)V");
-    gKeyphraseRecognitionExtraFields.id = GetFieldIDOrDie(env, gKeyphraseRecognitionExtraClass,
-                                                          "id", "I");
-    gKeyphraseRecognitionExtraFields.recognitionModes = GetFieldIDOrDie(env,
-            gKeyphraseRecognitionExtraClass, "recognitionModes", "I");
-    gKeyphraseRecognitionExtraFields.coarseConfidenceLevel = GetFieldIDOrDie(env,
-            gKeyphraseRecognitionExtraClass, "coarseConfidenceLevel", "I");
-    gKeyphraseRecognitionExtraFields.confidenceLevels = GetFieldIDOrDie(env,
-            gKeyphraseRecognitionExtraClass, "confidenceLevels",
-            "[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;");
-
-    jclass confidenceLevelClass = FindClassOrDie(env, kConfidenceLevelClassPathName);
-    gConfidenceLevelClass = MakeGlobalRefOrDie(env, confidenceLevelClass);
-    gConfidenceLevelCstor = GetMethodIDOrDie(env, confidenceLevelClass, "<init>", "(II)V");
-    gConfidenceLevelFields.userId = GetFieldIDOrDie(env, confidenceLevelClass, "userId", "I");
-    gConfidenceLevelFields.confidenceLevel = GetFieldIDOrDie(env, confidenceLevelClass,
-                                                             "confidenceLevel", "I");
-
-    jclass audioFormatClass = FindClassOrDie(env, kAudioFormatClassPathName);
-    gAudioFormatClass = MakeGlobalRefOrDie(env, audioFormatClass);
-    gAudioFormatCstor = GetMethodIDOrDie(env, audioFormatClass, "<init>", "(IIII)V");
-
-    jclass soundModelEventClass = FindClassOrDie(env, kSoundModelEventClassPathName);
-    gSoundModelEventClass = MakeGlobalRefOrDie(env, soundModelEventClass);
-    gSoundModelEventCstor = GetMethodIDOrDie(env, soundModelEventClass, "<init>", "(II[B)V");
-
-
-    RegisterMethodsOrDie(env, kSoundTriggerClassPathName, gMethods, NELEM(gMethods));
-    return RegisterMethodsOrDie(env, kModuleClassPathName, gModuleMethods, NELEM(gModuleMethods));
-}
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 342aba0..6cbc587 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -763,7 +763,7 @@
     }
 
     // get what we have for the metrics from the record session
-    MediaAnalyticsItem *item = NULL;
+    mediametrics::Item *item = NULL;
 
     status_t err = lpRecord->getMetrics(item);
     if (err != OK) {
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index c5049ec..c979133 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1032,7 +1032,7 @@
     }
 
     // get what we have for the metrics from the track
-    MediaAnalyticsItem *item = NULL;
+    mediametrics::Item *item = NULL;
 
     status_t err = lpTrack->getMetrics(item);
     if (err != OK) {
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 4733048..8c1ecae 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -33,6 +33,7 @@
 
 // Static whitelist of open paths that the zygote is allowed to keep open.
 static const char* kPathWhitelist[] = {
+  "/apex/com.android.appsearch/javalib/framework-appsearch.jar",
   "/apex/com.android.conscrypt/javalib/conscrypt.jar",
   "/apex/com.android.ipsec/javalib/ike.jar",
   "/apex/com.android.media/javalib/updatable-media.jar",
@@ -40,6 +41,7 @@
   "/apex/com.android.sdkext/javalib/framework-sdkext.jar",
   "/apex/com.android.telephony/javalib/telephony-common.jar",
   "/apex/com.android.telephony/javalib/ims-common.jar",
+  "/apex/com.android.wifi/javalib/framework-wifi.jar",
   "/dev/null",
   "/dev/socket/zygote",
   "/dev/socket/zygote_secondary",
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index ab97fdd..0c2129f 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2467,4 +2467,9 @@
     // CATEGORY: SETTINGS
     // OS: R
     ACCOUNT_WORK = 1807;
+
+    // OPEN: Settings > Developer Options > Bug report handler
+    // CATEGORY: SETTINGS
+    // OS: R
+    SETTINGS_BUGREPORT_HANDLER = 1808;
 }
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 4ea574d..6a1ec6c 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -219,6 +219,12 @@
     }
     optional Gesture gesture = 74;
 
+    message GestureNavigation {
+        optional SettingProto back_gesture_inset_scale_left = 1 [(android.privacy).dest = DEST_AUTOMATIC];
+        optional SettingProto back_gesture_inset_scale_right = 2 [(android.privacy).dest = DEST_AUTOMATIC];
+    }
+    optional GestureNavigation gesture_navigation = 77;
+
     optional SettingProto immersive_mode_confirmations = 24 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     message Incall {
@@ -316,6 +322,7 @@
     optional SettingProto multi_press_timeout = 38 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     optional SettingProto navigation_mode = 76 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     message NfcPayment {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
 
@@ -564,5 +571,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 77;
+    // Next tag = 78;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c925744..cb5b4a5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4410,6 +4410,12 @@
     <permission android:name="android.permission.MANAGE_SOUND_TRIGGER"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows preempting sound trigger recognitions for the sake of capturing audio on
+         implementations which do not support running both concurrently.
+         @hide -->
+    <permission android:name="android.permission.PREEMPT_SOUND_TRIGGER"
+                android:protectionLevel="signature|privileged" />
+
     <!-- Must be required by system/priv apps implementing sound trigger detection services
          @hide
          @SystemApi -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0046d82..03f8ebd 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2544,6 +2544,28 @@
     <string name="config_customAdbPublicKeyConfirmationSecondaryUserComponent"
             >com.android.systemui/com.android.systemui.usb.UsbDebuggingSecondaryUserActivity</string>
 
+    <!-- Component name of the activity that shows the usb containment status. -->
+    <string name="config_usbContaminantActivity" translatable="false"
+            >com.android.systemui/com.android.systemui.usb.UsbContaminantActivity</string>
+
+    <!-- Component name of the activity that shows the request for access to a usb device. -->
+    <string name="config_usbPermissionActivity" translatable="false"
+            >com.android.systemui/com.android.systemui.usb.UsbPermissionActivity</string>
+
+    <!-- Component name of the activity that shows more information about a usb accessory. -->
+    <string name="config_usbAccessoryUriActivity" translatable="false"
+            >com.android.systemui/com.android.systemui.usb.UsbAccessoryUriActivity</string>
+
+    <!-- Component name of the activity that confirms the activity to start when a usb device is
+         plugged in. -->
+    <string name="config_usbConfirmActivity" translatable="false"
+            >com.android.systemui/com.android.systemui.usb.UsbConfirmActivity</string>
+
+    <!-- Component name of the activity to select the activity to start when a usb device is plugged
+         in. -->
+    <string name="config_usbResolverActivity" translatable="false"
+            >com.android.systemui/com.android.systemui.usb.UsbResolverActivity</string>
+
     <!-- Name of the dialog that is used to request the user's consent to VPN connection -->
     <string name="config_customVpnConfirmDialogComponent" translatable="false"
             >com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog</string>
@@ -2674,11 +2696,45 @@
          property. If this is false, then the following recents config flags are ignored. -->
     <bool name="config_hasRecents">true</bool>
 
-    <!-- Component name for the activity that will be presenting the Recents UI, which will receive special permissions for API related
-          to fetching and presenting recent tasks. The default configuration uses Launcehr3QuickStep as default launcher and points to
-          the corresponding recents component. When using a different default launcher, change this appropriately or use the default
-          systemui implementation: com.android.systemui/.recents.RecentsActivity -->
-    <string name="config_recentsComponentName" translatable="false">com.android.launcher3/com.android.quickstep.RecentsActivity</string>
+    <!-- Component name for the activity that will be presenting the Recents UI, which will receive
+         special permissions for API related to fetching and presenting recent tasks. The default
+         configuration uses Launcehr3QuickStep as default launcher and points to the corresponding
+         recents component. When using a different default launcher, change this appropriately or
+         use the default systemui implementation: com.android.systemui/.recents.RecentsActivity -->
+    <string name="config_recentsComponentName" translatable="false"
+            >com.android.launcher3/com.android.quickstep.RecentsActivity</string>
+
+    <!-- SystemUi service component -->
+    <string name="config_systemUIServiceComponent" translatable="false"
+            >com.android.systemui/com.android.systemui.SystemUIService</string>
+
+    <!-- Keyguard component -->
+    <string name="config_keyguardComponent" translatable="false"
+            >com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>
+
+    <!-- Screen record dialog component -->
+    <string name="config_screenRecorderComponent" translatable="false"
+            >com.android.systemui/com.android.systemui.screenrecord.ScreenRecordDialog</string>
+
+    <!-- The component name of a special dock app that merely launches a dream.
+         We don't want to launch this app when docked because it causes an unnecessary
+         activity transition.  We just want to start the dream. -->
+    <string name="config_somnambulatorComponent" translatable="false"
+            >com.android.systemui/com.android.systemui.Somnambulator</string>
+
+    <!-- The component name of a special dock app that merely launches a dream.
+         We don't want to launch this app when docked because it causes an unnecessary
+         activity transition.  We just want to start the dream.. -->
+    <string name="config_screenshotServiceComponent" translatable="false"
+            >com.android.systemui/com.android.systemui.screenshot.TakeScreenshotService</string>
+
+    <!-- The component notified when there is an error while taking a screenshot. -->
+    <string name="config_screenshotErrorReceiverComponent" translatable="false"
+            >com.android.systemui/com.android.systemui.screenshot.ScreenshotServiceErrorReceiver</string>
+
+    <!-- The component for the activity shown to grant permissions for a slice. -->
+    <string name="config_slicePermissionComponent" translatable="false"
+            >com.android.systemui/com.android.systemui.SlicePermissionActivity</string>
 
     <!-- The minimum number of visible recent tasks to be presented to the user through the
          SystemUI. Can be -1 if there is no minimum limit. -->
@@ -2851,10 +2907,6 @@
     <!-- Whether to use voip audio mode for ims call -->
     <bool name="config_use_voip_mode_for_ims">false</bool>
 
-    <!-- ImsService package name to bind to by default. If none is specified in an overlay, an
-         empty string is passed in -->
-    <string name="config_ims_package"/>
-
     <!-- String array containing numbers that shouldn't be logged. Country-specific. -->
     <string-array name="unloggable_phone_numbers" />
 
@@ -3032,9 +3084,6 @@
     <!-- Specifies the maximum burn-in offset vertically. -->
     <integer name="config_burnInProtectionMaxVerticalOffset">0</integer>
 
-    <!-- Keyguard component -->
-    <string name="config_keyguardComponent" translatable="false">com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>
-
     <!-- Limit for the number of face templates per user -->
     <integer name="config_faceMaxTemplatesPerUser">1</integer>
 
@@ -3073,7 +3122,9 @@
 
          The path is assumed to be specified in display coordinates with pixel units and in
          the display's native orientation, with the origin of the coordinate system at the
-         center top of the display.
+         center top of the display. Optionally, you can append either `@left` or `@right` to the
+         end of the path string, in order to change the path origin to either the top left,
+         or top right of the display.
 
          To facilitate writing device-independent emulation overlays, the marker `@dp` can be
          appended after the path string to interpret coordinates in dp instead of px units.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index de1b5ba..ab10738 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4946,8 +4946,12 @@
     <!-- Resolver target actions strings -->
     <!-- Pin this app to the top of the Sharesheet app list. [CHAR LIMIT=60]-->
     <string name="pin_target">Pin</string>
+    <!-- Pin this app to the top of the Sharesheet app list. [CHAR LIMIT=60]-->
+    <string name="pin_specific_target">Pin <xliff:g id="label" example="Tweet">%1$s</xliff:g></string>
     <!-- Un-pin this app in the Sharesheet, so that it is sorted normally. [CHAR LIMIT=60]-->
-    <string name="unpin_target">Unpin</string>
+    <string name="unpin_target">Unpin </string>
+    <!-- Un-pin this app in the Sharesheet, so that it is sorted normally. [CHAR LIMIT=60]-->
+    <string name="unpin_specific_target">Unpin <xliff:g id="label" example="Tweet">%1$s</xliff:g></string>
     <!-- View application info for a target. -->
     <string name="app_info">App info</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1b6583d..a8d30c1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -295,7 +295,6 @@
   <java-symbol type="bool" name="config_enableBurnInProtection" />
   <java-symbol type="bool" name="config_hotswapCapable" />
   <java-symbol type="bool" name="config_mms_content_disposition_support" />
-  <java-symbol type="string" name="config_ims_package" />
   <java-symbol type="string" name="config_wwan_network_service_package" />
   <java-symbol type="string" name="config_wlan_network_service_package" />
   <java-symbol type="string" name="config_wwan_network_service_class" />
@@ -360,6 +359,17 @@
   <java-symbol type="bool" name="config_use16BitTaskSnapshotPixelFormat" />
   <java-symbol type="bool" name="config_hasRecents" />
   <java-symbol type="string" name="config_recentsComponentName" />
+  <java-symbol type="string" name="config_systemUIServiceComponent" />
+  <java-symbol type="string" name="config_screenRecorderComponent" />
+  <java-symbol type="string" name="config_somnambulatorComponent" />
+  <java-symbol type="string" name="config_screenshotServiceComponent" />
+  <java-symbol type="string" name="config_screenshotErrorReceiverComponent" />
+  <java-symbol type="string" name="config_slicePermissionComponent" />
+  <java-symbol type="string" name="config_usbContaminantActivity" />
+  <java-symbol type="string" name="config_usbPermissionActivity" />
+  <java-symbol type="string" name="config_usbAccessoryUriActivity" />
+  <java-symbol type="string" name="config_usbConfirmActivity" />
+  <java-symbol type="string" name="config_usbResolverActivity" />
   <java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
   <java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" />
   <java-symbol type="integer" name="config_minNumVisibleRecentTasks_grid" />
@@ -2947,6 +2957,8 @@
   <!-- Resolver target actions -->
   <java-symbol type="array" name="resolver_target_actions_pin" />
   <java-symbol type="array" name="resolver_target_actions_unpin" />
+  <java-symbol type="string" name="pin_specific_target" />
+  <java-symbol type="string" name="unpin_specific_target" />
 
   <java-symbol type="array" name="non_removable_euicc_slots" />
 
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 1a48260..cce38f6 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -19,11 +19,9 @@
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
-import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowInsets.Type.systemBars;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.eq;
@@ -118,7 +116,7 @@
         consumers.put(ITYPE_NAVIGATION_BAR, navConsumer);
         mController = new InsetsAnimationControlImpl(consumers,
                 new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
-                () -> mMockTransactionApplier, mMockController);
+                () -> mMockTransactionApplier, mMockController, 10 /* durationMs */);
     }
 
     @Test
@@ -131,7 +129,8 @@
 
     @Test
     public void testChangeInsets() {
-        mController.changeInsets(Insets.of(0, 30, 40, 0));
+        mController.setInsetsAndAlpha(Insets.of(0, 30, 40, 0), 1f /* alpha */,
+                0f /* fraction */);
         mController.applyChangeInsets(new InsetsState());
         assertEquals(Insets.of(0, 30, 40, 0), mController.getCurrentInsets());
 
@@ -150,24 +149,24 @@
     @Test
     public void testFinishing() {
         when(mMockController.getState()).thenReturn(mInsetsState);
-        mController.finish(navigationBars());
+        mController.finish(true /* shown */);
         mController.applyChangeInsets(mInsetsState);
-        assertFalse(mInsetsState.getSource(ITYPE_STATUS_BAR).isVisible());
+        assertTrue(mInsetsState.getSource(ITYPE_STATUS_BAR).isVisible());
         assertTrue(mInsetsState.getSource(ITYPE_NAVIGATION_BAR).isVisible());
-        assertEquals(Insets.of(0, 0, 100, 0), mController.getCurrentInsets());
-        verify(mMockController).notifyFinished(eq(mController), eq(navigationBars()));
+        assertEquals(Insets.of(0, 100, 100, 0), mController.getCurrentInsets());
+        verify(mMockController).notifyFinished(eq(mController), eq(true /* shown */));
     }
 
     @Test
     public void testCancelled() {
         mController.onCancelled();
         try {
-            mController.changeInsets(Insets.NONE);
+            mController.setInsetsAndAlpha(Insets.NONE, 1f /*alpha */, 0f /* fraction */);
             fail("Expected exception to be thrown");
         } catch (IllegalStateException ignored) {
         }
         verify(mMockListener).onCancelled();
-        mController.finish(navigationBars());
+        mController.finish(true /* shown */);
     }
 
     private void assertPosition(Matrix m, Rect original, Rect transformed) {
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index e4d8279..a89fc1e 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -123,7 +123,7 @@
 
         WindowInsetsAnimationControlListener mockListener =
                 mock(WindowInsetsAnimationControlListener.class);
-        mController.controlWindowInsetsAnimation(statusBars(), mockListener);
+        mController.controlWindowInsetsAnimation(statusBars(), 10 /* durationMs */, mockListener);
         verify(mockListener).onReady(any(), anyInt());
         mController.onControlsChanged(new InsetsSourceControl[0]);
         verify(mockListener).onCancelled();
@@ -135,7 +135,7 @@
         mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200));
         WindowInsetsAnimationControlListener controlListener =
                 mock(WindowInsetsAnimationControlListener.class);
-        mController.controlWindowInsetsAnimation(0, controlListener);
+        mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, controlListener);
         verify(controlListener).onCancelled();
         verify(controlListener, never()).onReady(any(), anyInt());
     }
@@ -331,12 +331,13 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener mockListener =
                     mock(WindowInsetsAnimationControlListener.class);
-            mController.controlWindowInsetsAnimation(statusBars(), mockListener);
+            mController.controlWindowInsetsAnimation(statusBars(), 0 /* durationMs */,
+                    mockListener);
 
             ArgumentCaptor<WindowInsetsAnimationController> controllerCaptor =
                     ArgumentCaptor.forClass(WindowInsetsAnimationController.class);
             verify(mockListener).onReady(controllerCaptor.capture(), anyInt());
-            controllerCaptor.getValue().finish(0 /* shownTypes */);
+            controllerCaptor.getValue().finish(false /* shown */);
         });
         waitUntilNextFrame();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
diff --git a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
index 9002c2c..ffc925f 100644
--- a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
+++ b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
@@ -111,7 +111,7 @@
     }
 
     @Test
-    public void testWriteToParcel_Exceptionally() throws Exception {
+    public void testWriteToParcel_Exception() throws Exception {
         Parcel parcel = Parcel.obtain();
         AndroidFuture<Integer> future1 = new AndroidFuture<>();
         future1.completeExceptionally(new UnsupportedOperationException());
@@ -123,4 +123,30 @@
                 expectThrows(ExecutionException.class, future2::get);
         assertThat(executionException.getCause()).isInstanceOf(UnsupportedOperationException.class);
     }
+
+    @Test
+    public void testWriteToParcel_Incomplete() throws Exception {
+        Parcel parcel = Parcel.obtain();
+        AndroidFuture<Integer> future1 = new AndroidFuture<>();
+        future1.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);
+        AndroidFuture future2 = AndroidFuture.CREATOR.createFromParcel(parcel);
+        future2.complete(5);
+        assertThat(future1.get()).isEqualTo(5);
+    }
+
+    @Test
+    public void testWriteToParcel_Incomplete_Exception() throws Exception {
+        Parcel parcel = Parcel.obtain();
+        AndroidFuture<Integer> future1 = new AndroidFuture<>();
+        future1.writeToParcel(parcel, 0);
+
+        parcel.setDataPosition(0);
+        AndroidFuture future2 = AndroidFuture.CREATOR.createFromParcel(parcel);
+        future2.completeExceptionally(new UnsupportedOperationException());
+        ExecutionException executionException =
+                expectThrows(ExecutionException.class, future1::get);
+        assertThat(executionException.getCause()).isInstanceOf(UnsupportedOperationException.class);
+    }
 }
diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
index e16d1ca..1472b90 100644
--- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
+++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
@@ -24,8 +24,13 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Looper;
 
@@ -54,9 +59,15 @@
         // This raises a `SecurityException` if the device is locked. Calling either `Context`
         // method results in a broadcast of `android.intent.action. USER_PRESENT`. Only the system
         // process is allowed to broadcast that `Intent`.
+        Resources res = mock(Resources.class);
         mContext = Mockito.spy(Context.class);
-        Mockito.doNothing().when(mContext).sendBroadcastAsUser(any(), any());
-        Mockito.doReturn(true).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any());
+        doNothing().when(mContext).sendBroadcastAsUser(any(), any());
+        doReturn(true).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any());
+        doReturn(res).when(mContext).getResources();
+        doReturn("com.android.systemui/.Service").when(res).getString(
+                eq(com.android.internal.R.string.config_screenshotServiceComponent));
+        doReturn("com.android.systemui/.ErrorReceiver").when(res).getString(
+                eq(com.android.internal.R.string.config_screenshotErrorReceiverComponent));
 
         mHandler = new Handler(Looper.getMainLooper());
         mScreenshotHelper = new ScreenshotHelper(mContext);
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 80098c5..0574775 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -157,6 +157,7 @@
     <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="audioserver" />
     <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" />
     <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="audioserver" />
+    <assign-permission name="android.permission.PREEMPT_SOUND_TRIGGER" uid="audioserver" />
 
     <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" />
     <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 322cbd7..e07edd4 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -145,7 +145,7 @@
         <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
         <permission name="android.permission.CHANGE_CONFIGURATION"/>
         <permission name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/>
-        <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+        <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
         <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
         <permission name="android.permission.DUMP"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
diff --git a/framework-jarjar-rules.txt b/framework-jarjar-rules.txt
new file mode 100644
index 0000000..d8af726
--- /dev/null
+++ b/framework-jarjar-rules.txt
@@ -0,0 +1,2 @@
+rule android.hidl.** android.internal.hidl.@1
+rule android.net.wifi.WifiAnnotations* android.internal.wifi.WifiAnnotations@1
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 2648008..5ad93f4 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.graphics.fonts.FontVariationAxis;
 import android.text.TextUtils;
@@ -57,6 +58,7 @@
      *
      * This cannot be deleted because it's in use by AndroidX.
      */
+    @UnsupportedAppUsage(trackingBug = 123768928)
     public long mNativePtr;
 
     // Points native font family builder. Must be zero after freezing this family.
@@ -65,6 +67,7 @@
     /**
      * This cannot be deleted because it's in use by AndroidX.
      */
+    @UnsupportedAppUsage(trackingBug = 123768928)
     public FontFamily() {
         mBuilderPtr = nInitBuilder(null, 0);
         mNativeBuilderCleaner = sBuilderRegistry.registerNativeAllocation(this, mBuilderPtr);
@@ -73,6 +76,7 @@
     /**
      * This cannot be deleted because it's in use by AndroidX.
      */
+    @UnsupportedAppUsage(trackingBug = 123768928)
     public FontFamily(@Nullable String[] langs, int variant) {
         final String langsString;
         if (langs == null || langs.length == 0) {
@@ -94,6 +98,7 @@
      *
      * This cannot be deleted because it's in use by AndroidX.
      */
+    @UnsupportedAppUsage(trackingBug = 123768928)
     public boolean freeze() {
         if (mBuilderPtr == 0) {
             throw new IllegalStateException("This FontFamily is already frozen");
@@ -110,6 +115,7 @@
     /**
      * This cannot be deleted because it's in use by AndroidX.
      */
+    @UnsupportedAppUsage(trackingBug = 123768928)
     public void abortCreation() {
         if (mBuilderPtr == 0) {
             throw new IllegalStateException("This FontFamily is already frozen or abandoned");
@@ -121,6 +127,7 @@
     /**
      * This cannot be deleted because it's in use by AndroidX.
      */
+    @UnsupportedAppUsage(trackingBug = 123768928)
     public boolean addFont(String path, int ttcIndex, FontVariationAxis[] axes, int weight,
             int italic) {
         if (mBuilderPtr == 0) {
@@ -144,6 +151,7 @@
     /**
      * This cannot be deleted because it's in use by AndroidX.
      */
+    @UnsupportedAppUsage(trackingBug = 123768928)
     public boolean addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes,
             int weight, int italic) {
         if (mBuilderPtr == 0) {
@@ -171,6 +179,7 @@
      *
      * This cannot be deleted because it's in use by AndroidX.
      */
+    @UnsupportedAppUsage(trackingBug = 123768928)
     public boolean addFontFromAssetManager(AssetManager mgr, String path, int cookie,
             boolean isAsset, int ttcIndex, int weight, int isItalic,
             FontVariationAxis[] axes) {
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 45b2de5..c6586ec 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -784,9 +784,7 @@
             mFillPaint.setDither(st.mDither);
             mFillPaint.setColorFilter(colorFilter);
             if (colorFilter != null && st.mSolidColors == null) {
-                // If we don't have a solid color and we don't have a gradient,
-                // the app is stroking the shape, set the color to transparent
-                mFillPaint.setColor(st.mGradientColors != null ? mAlpha << 24 : 0);
+                mFillPaint.setColor(mAlpha << 24);
             }
             if (haveStroke) {
                 mStrokePaint.setAlpha(currStrokeAlpha);
diff --git a/jarjar_rules_hidl.txt b/jarjar_rules_hidl.txt
deleted file mode 100644
index 4b2331d..0000000
--- a/jarjar_rules_hidl.txt
+++ /dev/null
@@ -1 +0,0 @@
-rule android.hidl.** android.internal.hidl.@1
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 572fa8c..048dee6 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -65,6 +65,9 @@
     /** Key prefix for VPN. */
     public static final String VPN = "VPN_";
 
+    /** Key prefix for platform VPNs. */
+    public static final String PLATFORM_VPN = "PLATFORM_VPN_";
+
     /** Key prefix for WIFI. */
     public static final String WIFI = "WIFI_";
 
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index c276a23..c462eb7 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -10,1439 +10,1466 @@
     /* 6  */ {'B', 'a', 's', 's'},
     /* 7  */ {'B', 'e', 'n', 'g'},
     /* 8  */ {'B', 'r', 'a', 'h'},
-    /* 9  */ {'C', 'a', 'n', 's'},
-    /* 10 */ {'C', 'a', 'r', 'i'},
-    /* 11 */ {'C', 'h', 'a', 'm'},
-    /* 12 */ {'C', 'h', 'e', 'r'},
-    /* 13 */ {'C', 'o', 'p', 't'},
-    /* 14 */ {'C', 'p', 'r', 't'},
-    /* 15 */ {'C', 'y', 'r', 'l'},
-    /* 16 */ {'D', 'e', 'v', 'a'},
-    /* 17 */ {'E', 'g', 'y', 'p'},
-    /* 18 */ {'E', 't', 'h', 'i'},
-    /* 19 */ {'G', 'e', 'o', 'r'},
-    /* 20 */ {'G', 'o', 't', 'h'},
-    /* 21 */ {'G', 'r', 'e', 'k'},
-    /* 22 */ {'G', 'u', 'j', 'r'},
-    /* 23 */ {'G', 'u', 'r', 'u'},
-    /* 24 */ {'H', 'a', 'n', 's'},
-    /* 25 */ {'H', 'a', 'n', 't'},
-    /* 26 */ {'H', 'a', 't', 'r'},
-    /* 27 */ {'H', 'e', 'b', 'r'},
-    /* 28 */ {'H', 'l', 'u', 'w'},
-    /* 29 */ {'H', 'm', 'n', 'g'},
-    /* 30 */ {'I', 't', 'a', 'l'},
-    /* 31 */ {'J', 'p', 'a', 'n'},
-    /* 32 */ {'K', 'a', 'l', 'i'},
-    /* 33 */ {'K', 'a', 'n', 'a'},
-    /* 34 */ {'K', 'h', 'a', 'r'},
-    /* 35 */ {'K', 'h', 'm', 'r'},
-    /* 36 */ {'K', 'n', 'd', 'a'},
-    /* 37 */ {'K', 'o', 'r', 'e'},
-    /* 38 */ {'L', 'a', 'n', 'a'},
-    /* 39 */ {'L', 'a', 'o', 'o'},
-    /* 40 */ {'L', 'a', 't', 'n'},
-    /* 41 */ {'L', 'e', 'p', 'c'},
-    /* 42 */ {'L', 'i', 'n', 'a'},
-    /* 43 */ {'L', 'i', 's', 'u'},
-    /* 44 */ {'L', 'y', 'c', 'i'},
-    /* 45 */ {'L', 'y', 'd', 'i'},
-    /* 46 */ {'M', 'a', 'n', 'd'},
-    /* 47 */ {'M', 'a', 'n', 'i'},
-    /* 48 */ {'M', 'e', 'r', 'c'},
-    /* 49 */ {'M', 'l', 'y', 'm'},
-    /* 50 */ {'M', 'o', 'n', 'g'},
-    /* 51 */ {'M', 'r', 'o', 'o'},
-    /* 52 */ {'M', 'y', 'm', 'r'},
-    /* 53 */ {'N', 'a', 'r', 'b'},
-    /* 54 */ {'N', 'k', 'o', 'o'},
-    /* 55 */ {'O', 'g', 'a', 'm'},
-    /* 56 */ {'O', 'r', 'k', 'h'},
-    /* 57 */ {'O', 'r', 'y', 'a'},
-    /* 58 */ {'O', 's', 'g', 'e'},
-    /* 59 */ {'P', 'a', 'u', 'c'},
-    /* 60 */ {'P', 'h', 'l', 'i'},
-    /* 61 */ {'P', 'h', 'n', 'x'},
-    /* 62 */ {'P', 'l', 'r', 'd'},
-    /* 63 */ {'P', 'r', 't', 'i'},
-    /* 64 */ {'R', 'u', 'n', 'r'},
-    /* 65 */ {'S', 'a', 'm', 'r'},
-    /* 66 */ {'S', 'a', 'r', 'b'},
-    /* 67 */ {'S', 'a', 'u', 'r'},
-    /* 68 */ {'S', 'g', 'n', 'w'},
-    /* 69 */ {'S', 'i', 'n', 'h'},
-    /* 70 */ {'S', 'o', 'r', 'a'},
-    /* 71 */ {'S', 'y', 'r', 'c'},
-    /* 72 */ {'T', 'a', 'l', 'e'},
-    /* 73 */ {'T', 'a', 'l', 'u'},
-    /* 74 */ {'T', 'a', 'm', 'l'},
-    /* 75 */ {'T', 'a', 'n', 'g'},
-    /* 76 */ {'T', 'a', 'v', 't'},
-    /* 77 */ {'T', 'e', 'l', 'u'},
-    /* 78 */ {'T', 'f', 'n', 'g'},
-    /* 79 */ {'T', 'h', 'a', 'a'},
-    /* 80 */ {'T', 'h', 'a', 'i'},
-    /* 81 */ {'T', 'i', 'b', 't'},
-    /* 82 */ {'U', 'g', 'a', 'r'},
-    /* 83 */ {'V', 'a', 'i', 'i'},
-    /* 84 */ {'X', 'p', 'e', 'o'},
-    /* 85 */ {'X', 's', 'u', 'x'},
-    /* 86 */ {'Y', 'i', 'i', 'i'},
-    /* 87 */ {'~', '~', '~', 'A'},
-    /* 88 */ {'~', '~', '~', 'B'},
+    /* 9  */ {'C', 'a', 'k', 'm'},
+    /* 10 */ {'C', 'a', 'n', 's'},
+    /* 11 */ {'C', 'a', 'r', 'i'},
+    /* 12 */ {'C', 'h', 'a', 'm'},
+    /* 13 */ {'C', 'h', 'e', 'r'},
+    /* 14 */ {'C', 'o', 'p', 't'},
+    /* 15 */ {'C', 'p', 'r', 't'},
+    /* 16 */ {'C', 'y', 'r', 'l'},
+    /* 17 */ {'D', 'e', 'v', 'a'},
+    /* 18 */ {'E', 'g', 'y', 'p'},
+    /* 19 */ {'E', 't', 'h', 'i'},
+    /* 20 */ {'G', 'e', 'o', 'r'},
+    /* 21 */ {'G', 'o', 'n', 'g'},
+    /* 22 */ {'G', 'o', 'n', 'm'},
+    /* 23 */ {'G', 'o', 't', 'h'},
+    /* 24 */ {'G', 'r', 'e', 'k'},
+    /* 25 */ {'G', 'u', 'j', 'r'},
+    /* 26 */ {'G', 'u', 'r', 'u'},
+    /* 27 */ {'H', 'a', 'n', 's'},
+    /* 28 */ {'H', 'a', 'n', 't'},
+    /* 29 */ {'H', 'a', 't', 'r'},
+    /* 30 */ {'H', 'e', 'b', 'r'},
+    /* 31 */ {'H', 'l', 'u', 'w'},
+    /* 32 */ {'H', 'm', 'n', 'g'},
+    /* 33 */ {'H', 'm', 'n', 'p'},
+    /* 34 */ {'I', 't', 'a', 'l'},
+    /* 35 */ {'J', 'p', 'a', 'n'},
+    /* 36 */ {'K', 'a', 'l', 'i'},
+    /* 37 */ {'K', 'a', 'n', 'a'},
+    /* 38 */ {'K', 'h', 'a', 'r'},
+    /* 39 */ {'K', 'h', 'm', 'r'},
+    /* 40 */ {'K', 'n', 'd', 'a'},
+    /* 41 */ {'K', 'o', 'r', 'e'},
+    /* 42 */ {'L', 'a', 'n', 'a'},
+    /* 43 */ {'L', 'a', 'o', 'o'},
+    /* 44 */ {'L', 'a', 't', 'n'},
+    /* 45 */ {'L', 'e', 'p', 'c'},
+    /* 46 */ {'L', 'i', 'n', 'a'},
+    /* 47 */ {'L', 'i', 's', 'u'},
+    /* 48 */ {'L', 'y', 'c', 'i'},
+    /* 49 */ {'L', 'y', 'd', 'i'},
+    /* 50 */ {'M', 'a', 'n', 'd'},
+    /* 51 */ {'M', 'a', 'n', 'i'},
+    /* 52 */ {'M', 'e', 'r', 'c'},
+    /* 53 */ {'M', 'l', 'y', 'm'},
+    /* 54 */ {'M', 'o', 'n', 'g'},
+    /* 55 */ {'M', 'r', 'o', 'o'},
+    /* 56 */ {'M', 'y', 'm', 'r'},
+    /* 57 */ {'N', 'a', 'r', 'b'},
+    /* 58 */ {'N', 'k', 'o', 'o'},
+    /* 59 */ {'N', 's', 'h', 'u'},
+    /* 60 */ {'O', 'g', 'a', 'm'},
+    /* 61 */ {'O', 'r', 'k', 'h'},
+    /* 62 */ {'O', 'r', 'y', 'a'},
+    /* 63 */ {'O', 's', 'g', 'e'},
+    /* 64 */ {'P', 'a', 'u', 'c'},
+    /* 65 */ {'P', 'h', 'l', 'i'},
+    /* 66 */ {'P', 'h', 'n', 'x'},
+    /* 67 */ {'P', 'l', 'r', 'd'},
+    /* 68 */ {'P', 'r', 't', 'i'},
+    /* 69 */ {'R', 'u', 'n', 'r'},
+    /* 70 */ {'S', 'a', 'm', 'r'},
+    /* 71 */ {'S', 'a', 'r', 'b'},
+    /* 72 */ {'S', 'a', 'u', 'r'},
+    /* 73 */ {'S', 'g', 'n', 'w'},
+    /* 74 */ {'S', 'i', 'n', 'h'},
+    /* 75 */ {'S', 'o', 'g', 'd'},
+    /* 76 */ {'S', 'o', 'r', 'a'},
+    /* 77 */ {'S', 'o', 'y', 'o'},
+    /* 78 */ {'S', 'y', 'r', 'c'},
+    /* 79 */ {'T', 'a', 'l', 'e'},
+    /* 80 */ {'T', 'a', 'l', 'u'},
+    /* 81 */ {'T', 'a', 'm', 'l'},
+    /* 82 */ {'T', 'a', 'n', 'g'},
+    /* 83 */ {'T', 'a', 'v', 't'},
+    /* 84 */ {'T', 'e', 'l', 'u'},
+    /* 85 */ {'T', 'f', 'n', 'g'},
+    /* 86 */ {'T', 'h', 'a', 'a'},
+    /* 87 */ {'T', 'h', 'a', 'i'},
+    /* 88 */ {'T', 'i', 'b', 't'},
+    /* 89 */ {'U', 'g', 'a', 'r'},
+    /* 90 */ {'V', 'a', 'i', 'i'},
+    /* 91 */ {'W', 'c', 'h', 'o'},
+    /* 92 */ {'X', 'p', 'e', 'o'},
+    /* 93 */ {'X', 's', 'u', 'x'},
+    /* 94 */ {'Y', 'i', 'i', 'i'},
+    /* 95 */ {'~', '~', '~', 'A'},
+    /* 96 */ {'~', '~', '~', 'B'},
 };
 
 
 const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
-    {0x61610000u, 40u}, // aa -> Latn
-    {0xA0000000u, 40u}, // aai -> Latn
-    {0xA8000000u, 40u}, // aak -> Latn
-    {0xD0000000u, 40u}, // aau -> Latn
-    {0x61620000u, 15u}, // ab -> Cyrl
-    {0xA0200000u, 40u}, // abi -> Latn
-    {0xC4200000u, 40u}, // abr -> Latn
-    {0xCC200000u, 40u}, // abt -> Latn
-    {0xE0200000u, 40u}, // aby -> Latn
-    {0x8C400000u, 40u}, // acd -> Latn
-    {0x90400000u, 40u}, // ace -> Latn
-    {0x9C400000u, 40u}, // ach -> Latn
-    {0x80600000u, 40u}, // ada -> Latn
-    {0x90600000u, 40u}, // ade -> Latn
-    {0xA4600000u, 40u}, // adj -> Latn
-    {0xE0600000u, 15u}, // ady -> Cyrl
-    {0xE4600000u, 40u}, // adz -> Latn
+    {0x61610000u, 44u}, // aa -> Latn
+    {0xA0000000u, 44u}, // aai -> Latn
+    {0xA8000000u, 44u}, // aak -> Latn
+    {0xD0000000u, 44u}, // aau -> Latn
+    {0x61620000u, 16u}, // ab -> Cyrl
+    {0xA0200000u, 44u}, // abi -> Latn
+    {0xC0200000u, 16u}, // abq -> Cyrl
+    {0xC4200000u, 44u}, // abr -> Latn
+    {0xCC200000u, 44u}, // abt -> Latn
+    {0xE0200000u, 44u}, // aby -> Latn
+    {0x8C400000u, 44u}, // acd -> Latn
+    {0x90400000u, 44u}, // ace -> Latn
+    {0x9C400000u, 44u}, // ach -> Latn
+    {0x80600000u, 44u}, // ada -> Latn
+    {0x90600000u, 44u}, // ade -> Latn
+    {0xA4600000u, 44u}, // adj -> Latn
+    {0xE0600000u, 16u}, // ady -> Cyrl
+    {0xE4600000u, 44u}, // adz -> Latn
     {0x61650000u,  4u}, // ae -> Avst
     {0x84800000u,  1u}, // aeb -> Arab
-    {0xE0800000u, 40u}, // aey -> Latn
-    {0x61660000u, 40u}, // af -> Latn
-    {0x88C00000u, 40u}, // agc -> Latn
-    {0x8CC00000u, 40u}, // agd -> Latn
-    {0x98C00000u, 40u}, // agg -> Latn
-    {0xB0C00000u, 40u}, // agm -> Latn
-    {0xB8C00000u, 40u}, // ago -> Latn
-    {0xC0C00000u, 40u}, // agq -> Latn
-    {0x80E00000u, 40u}, // aha -> Latn
-    {0xACE00000u, 40u}, // ahl -> Latn
+    {0xE0800000u, 44u}, // aey -> Latn
+    {0x61660000u, 44u}, // af -> Latn
+    {0x88C00000u, 44u}, // agc -> Latn
+    {0x8CC00000u, 44u}, // agd -> Latn
+    {0x98C00000u, 44u}, // agg -> Latn
+    {0xB0C00000u, 44u}, // agm -> Latn
+    {0xB8C00000u, 44u}, // ago -> Latn
+    {0xC0C00000u, 44u}, // agq -> Latn
+    {0x80E00000u, 44u}, // aha -> Latn
+    {0xACE00000u, 44u}, // ahl -> Latn
     {0xB8E00000u,  0u}, // aho -> Ahom
-    {0x99200000u, 40u}, // ajg -> Latn
-    {0x616B0000u, 40u}, // ak -> Latn
-    {0xA9400000u, 85u}, // akk -> Xsux
-    {0x81600000u, 40u}, // ala -> Latn
-    {0xA1600000u, 40u}, // ali -> Latn
-    {0xB5600000u, 40u}, // aln -> Latn
-    {0xCD600000u, 15u}, // alt -> Cyrl
-    {0x616D0000u, 18u}, // am -> Ethi
-    {0xB1800000u, 40u}, // amm -> Latn
-    {0xB5800000u, 40u}, // amn -> Latn
-    {0xB9800000u, 40u}, // amo -> Latn
-    {0xBD800000u, 40u}, // amp -> Latn
-    {0x89A00000u, 40u}, // anc -> Latn
-    {0xA9A00000u, 40u}, // ank -> Latn
-    {0xB5A00000u, 40u}, // ann -> Latn
-    {0xE1A00000u, 40u}, // any -> Latn
-    {0xA5C00000u, 40u}, // aoj -> Latn
-    {0xB1C00000u, 40u}, // aom -> Latn
-    {0xE5C00000u, 40u}, // aoz -> Latn
+    {0x99200000u, 44u}, // ajg -> Latn
+    {0x616B0000u, 44u}, // ak -> Latn
+    {0xA9400000u, 93u}, // akk -> Xsux
+    {0x81600000u, 44u}, // ala -> Latn
+    {0xA1600000u, 44u}, // ali -> Latn
+    {0xB5600000u, 44u}, // aln -> Latn
+    {0xCD600000u, 16u}, // alt -> Cyrl
+    {0x616D0000u, 19u}, // am -> Ethi
+    {0xB1800000u, 44u}, // amm -> Latn
+    {0xB5800000u, 44u}, // amn -> Latn
+    {0xB9800000u, 44u}, // amo -> Latn
+    {0xBD800000u, 44u}, // amp -> Latn
+    {0x89A00000u, 44u}, // anc -> Latn
+    {0xA9A00000u, 44u}, // ank -> Latn
+    {0xB5A00000u, 44u}, // ann -> Latn
+    {0xE1A00000u, 44u}, // any -> Latn
+    {0xA5C00000u, 44u}, // aoj -> Latn
+    {0xB1C00000u, 44u}, // aom -> Latn
+    {0xE5C00000u, 44u}, // aoz -> Latn
     {0x89E00000u,  1u}, // apc -> Arab
     {0x8DE00000u,  1u}, // apd -> Arab
-    {0x91E00000u, 40u}, // ape -> Latn
-    {0xC5E00000u, 40u}, // apr -> Latn
-    {0xC9E00000u, 40u}, // aps -> Latn
-    {0xE5E00000u, 40u}, // apz -> Latn
+    {0x91E00000u, 44u}, // ape -> Latn
+    {0xC5E00000u, 44u}, // apr -> Latn
+    {0xC9E00000u, 44u}, // aps -> Latn
+    {0xE5E00000u, 44u}, // apz -> Latn
     {0x61720000u,  1u}, // ar -> Arab
-    {0x61725842u, 88u}, // ar-XB -> ~~~B
+    {0x61725842u, 96u}, // ar-XB -> ~~~B
     {0x8A200000u,  2u}, // arc -> Armi
-    {0x9E200000u, 40u}, // arh -> Latn
-    {0xB6200000u, 40u}, // arn -> Latn
-    {0xBA200000u, 40u}, // aro -> Latn
+    {0x9E200000u, 44u}, // arh -> Latn
+    {0xB6200000u, 44u}, // arn -> Latn
+    {0xBA200000u, 44u}, // aro -> Latn
     {0xC2200000u,  1u}, // arq -> Arab
     {0xE2200000u,  1u}, // ary -> Arab
     {0xE6200000u,  1u}, // arz -> Arab
     {0x61730000u,  7u}, // as -> Beng
-    {0x82400000u, 40u}, // asa -> Latn
-    {0x92400000u, 68u}, // ase -> Sgnw
-    {0x9A400000u, 40u}, // asg -> Latn
-    {0xBA400000u, 40u}, // aso -> Latn
-    {0xCE400000u, 40u}, // ast -> Latn
-    {0x82600000u, 40u}, // ata -> Latn
-    {0x9A600000u, 40u}, // atg -> Latn
-    {0xA6600000u, 40u}, // atj -> Latn
-    {0xE2800000u, 40u}, // auy -> Latn
-    {0x61760000u, 15u}, // av -> Cyrl
+    {0x82400000u, 44u}, // asa -> Latn
+    {0x92400000u, 73u}, // ase -> Sgnw
+    {0x9A400000u, 44u}, // asg -> Latn
+    {0xBA400000u, 44u}, // aso -> Latn
+    {0xCE400000u, 44u}, // ast -> Latn
+    {0x82600000u, 44u}, // ata -> Latn
+    {0x9A600000u, 44u}, // atg -> Latn
+    {0xA6600000u, 44u}, // atj -> Latn
+    {0xE2800000u, 44u}, // auy -> Latn
+    {0x61760000u, 16u}, // av -> Cyrl
     {0xAEA00000u,  1u}, // avl -> Arab
-    {0xB6A00000u, 40u}, // avn -> Latn
-    {0xCEA00000u, 40u}, // avt -> Latn
-    {0xD2A00000u, 40u}, // avu -> Latn
-    {0x82C00000u, 16u}, // awa -> Deva
-    {0x86C00000u, 40u}, // awb -> Latn
-    {0xBAC00000u, 40u}, // awo -> Latn
-    {0xDEC00000u, 40u}, // awx -> Latn
-    {0x61790000u, 40u}, // ay -> Latn
-    {0x87000000u, 40u}, // ayb -> Latn
-    {0x617A0000u, 40u}, // az -> Latn
+    {0xB6A00000u, 44u}, // avn -> Latn
+    {0xCEA00000u, 44u}, // avt -> Latn
+    {0xD2A00000u, 44u}, // avu -> Latn
+    {0x82C00000u, 17u}, // awa -> Deva
+    {0x86C00000u, 44u}, // awb -> Latn
+    {0xBAC00000u, 44u}, // awo -> Latn
+    {0xDEC00000u, 44u}, // awx -> Latn
+    {0x61790000u, 44u}, // ay -> Latn
+    {0x87000000u, 44u}, // ayb -> Latn
+    {0x617A0000u, 44u}, // az -> Latn
     {0x617A4951u,  1u}, // az-IQ -> Arab
     {0x617A4952u,  1u}, // az-IR -> Arab
-    {0x617A5255u, 15u}, // az-RU -> Cyrl
-    {0x62610000u, 15u}, // ba -> Cyrl
+    {0x617A5255u, 16u}, // az-RU -> Cyrl
+    {0x62610000u, 16u}, // ba -> Cyrl
     {0xAC010000u,  1u}, // bal -> Arab
-    {0xB4010000u, 40u}, // ban -> Latn
-    {0xBC010000u, 16u}, // bap -> Deva
-    {0xC4010000u, 40u}, // bar -> Latn
-    {0xC8010000u, 40u}, // bas -> Latn
-    {0xD4010000u, 40u}, // bav -> Latn
+    {0xB4010000u, 44u}, // ban -> Latn
+    {0xBC010000u, 17u}, // bap -> Deva
+    {0xC4010000u, 44u}, // bar -> Latn
+    {0xC8010000u, 44u}, // bas -> Latn
+    {0xD4010000u, 44u}, // bav -> Latn
     {0xDC010000u,  5u}, // bax -> Bamu
-    {0x80210000u, 40u}, // bba -> Latn
-    {0x84210000u, 40u}, // bbb -> Latn
-    {0x88210000u, 40u}, // bbc -> Latn
-    {0x8C210000u, 40u}, // bbd -> Latn
-    {0xA4210000u, 40u}, // bbj -> Latn
-    {0xBC210000u, 40u}, // bbp -> Latn
-    {0xC4210000u, 40u}, // bbr -> Latn
-    {0x94410000u, 40u}, // bcf -> Latn
-    {0x9C410000u, 40u}, // bch -> Latn
-    {0xA0410000u, 40u}, // bci -> Latn
-    {0xB0410000u, 40u}, // bcm -> Latn
-    {0xB4410000u, 40u}, // bcn -> Latn
-    {0xB8410000u, 40u}, // bco -> Latn
-    {0xC0410000u, 18u}, // bcq -> Ethi
-    {0xD0410000u, 40u}, // bcu -> Latn
-    {0x8C610000u, 40u}, // bdd -> Latn
-    {0x62650000u, 15u}, // be -> Cyrl
-    {0x94810000u, 40u}, // bef -> Latn
-    {0x9C810000u, 40u}, // beh -> Latn
+    {0x80210000u, 44u}, // bba -> Latn
+    {0x84210000u, 44u}, // bbb -> Latn
+    {0x88210000u, 44u}, // bbc -> Latn
+    {0x8C210000u, 44u}, // bbd -> Latn
+    {0xA4210000u, 44u}, // bbj -> Latn
+    {0xBC210000u, 44u}, // bbp -> Latn
+    {0xC4210000u, 44u}, // bbr -> Latn
+    {0x94410000u, 44u}, // bcf -> Latn
+    {0x9C410000u, 44u}, // bch -> Latn
+    {0xA0410000u, 44u}, // bci -> Latn
+    {0xB0410000u, 44u}, // bcm -> Latn
+    {0xB4410000u, 44u}, // bcn -> Latn
+    {0xB8410000u, 44u}, // bco -> Latn
+    {0xC0410000u, 19u}, // bcq -> Ethi
+    {0xD0410000u, 44u}, // bcu -> Latn
+    {0x8C610000u, 44u}, // bdd -> Latn
+    {0x62650000u, 16u}, // be -> Cyrl
+    {0x94810000u, 44u}, // bef -> Latn
+    {0x9C810000u, 44u}, // beh -> Latn
     {0xA4810000u,  1u}, // bej -> Arab
-    {0xB0810000u, 40u}, // bem -> Latn
-    {0xCC810000u, 40u}, // bet -> Latn
-    {0xD8810000u, 40u}, // bew -> Latn
-    {0xDC810000u, 40u}, // bex -> Latn
-    {0xE4810000u, 40u}, // bez -> Latn
-    {0x8CA10000u, 40u}, // bfd -> Latn
-    {0xC0A10000u, 74u}, // bfq -> Taml
+    {0xB0810000u, 44u}, // bem -> Latn
+    {0xCC810000u, 44u}, // bet -> Latn
+    {0xD8810000u, 44u}, // bew -> Latn
+    {0xDC810000u, 44u}, // bex -> Latn
+    {0xE4810000u, 44u}, // bez -> Latn
+    {0x8CA10000u, 44u}, // bfd -> Latn
+    {0xC0A10000u, 81u}, // bfq -> Taml
     {0xCCA10000u,  1u}, // bft -> Arab
-    {0xE0A10000u, 16u}, // bfy -> Deva
-    {0x62670000u, 15u}, // bg -> Cyrl
-    {0x88C10000u, 16u}, // bgc -> Deva
+    {0xE0A10000u, 17u}, // bfy -> Deva
+    {0x62670000u, 16u}, // bg -> Cyrl
+    {0x88C10000u, 17u}, // bgc -> Deva
     {0xB4C10000u,  1u}, // bgn -> Arab
-    {0xDCC10000u, 21u}, // bgx -> Grek
-    {0x84E10000u, 16u}, // bhb -> Deva
-    {0x98E10000u, 40u}, // bhg -> Latn
-    {0xA0E10000u, 16u}, // bhi -> Deva
-    {0xA8E10000u, 40u}, // bhk -> Latn
-    {0xACE10000u, 40u}, // bhl -> Latn
-    {0xB8E10000u, 16u}, // bho -> Deva
-    {0xE0E10000u, 40u}, // bhy -> Latn
-    {0x62690000u, 40u}, // bi -> Latn
-    {0x85010000u, 40u}, // bib -> Latn
-    {0x99010000u, 40u}, // big -> Latn
-    {0xA9010000u, 40u}, // bik -> Latn
-    {0xB1010000u, 40u}, // bim -> Latn
-    {0xB5010000u, 40u}, // bin -> Latn
-    {0xB9010000u, 40u}, // bio -> Latn
-    {0xC1010000u, 40u}, // biq -> Latn
-    {0x9D210000u, 40u}, // bjh -> Latn
-    {0xA1210000u, 18u}, // bji -> Ethi
-    {0xA5210000u, 16u}, // bjj -> Deva
-    {0xB5210000u, 40u}, // bjn -> Latn
-    {0xB9210000u, 40u}, // bjo -> Latn
-    {0xC5210000u, 40u}, // bjr -> Latn
-    {0xE5210000u, 40u}, // bjz -> Latn
-    {0x89410000u, 40u}, // bkc -> Latn
-    {0xB1410000u, 40u}, // bkm -> Latn
-    {0xC1410000u, 40u}, // bkq -> Latn
-    {0xD1410000u, 40u}, // bku -> Latn
-    {0xD5410000u, 40u}, // bkv -> Latn
-    {0xCD610000u, 76u}, // blt -> Tavt
-    {0x626D0000u, 40u}, // bm -> Latn
-    {0x9D810000u, 40u}, // bmh -> Latn
-    {0xA9810000u, 40u}, // bmk -> Latn
-    {0xC1810000u, 40u}, // bmq -> Latn
-    {0xD1810000u, 40u}, // bmu -> Latn
+    {0xDCC10000u, 24u}, // bgx -> Grek
+    {0x84E10000u, 17u}, // bhb -> Deva
+    {0x98E10000u, 44u}, // bhg -> Latn
+    {0xA0E10000u, 17u}, // bhi -> Deva
+    {0xA8E10000u, 44u}, // bhk -> Latn
+    {0xACE10000u, 44u}, // bhl -> Latn
+    {0xB8E10000u, 17u}, // bho -> Deva
+    {0xE0E10000u, 44u}, // bhy -> Latn
+    {0x62690000u, 44u}, // bi -> Latn
+    {0x85010000u, 44u}, // bib -> Latn
+    {0x99010000u, 44u}, // big -> Latn
+    {0xA9010000u, 44u}, // bik -> Latn
+    {0xB1010000u, 44u}, // bim -> Latn
+    {0xB5010000u, 44u}, // bin -> Latn
+    {0xB9010000u, 44u}, // bio -> Latn
+    {0xC1010000u, 44u}, // biq -> Latn
+    {0x9D210000u, 44u}, // bjh -> Latn
+    {0xA1210000u, 19u}, // bji -> Ethi
+    {0xA5210000u, 17u}, // bjj -> Deva
+    {0xB5210000u, 44u}, // bjn -> Latn
+    {0xB9210000u, 44u}, // bjo -> Latn
+    {0xC5210000u, 44u}, // bjr -> Latn
+    {0xCD210000u, 44u}, // bjt -> Latn
+    {0xE5210000u, 44u}, // bjz -> Latn
+    {0x89410000u, 44u}, // bkc -> Latn
+    {0xB1410000u, 44u}, // bkm -> Latn
+    {0xC1410000u, 44u}, // bkq -> Latn
+    {0xD1410000u, 44u}, // bku -> Latn
+    {0xD5410000u, 44u}, // bkv -> Latn
+    {0xCD610000u, 83u}, // blt -> Tavt
+    {0x626D0000u, 44u}, // bm -> Latn
+    {0x9D810000u, 44u}, // bmh -> Latn
+    {0xA9810000u, 44u}, // bmk -> Latn
+    {0xC1810000u, 44u}, // bmq -> Latn
+    {0xD1810000u, 44u}, // bmu -> Latn
     {0x626E0000u,  7u}, // bn -> Beng
-    {0x99A10000u, 40u}, // bng -> Latn
-    {0xB1A10000u, 40u}, // bnm -> Latn
-    {0xBDA10000u, 40u}, // bnp -> Latn
-    {0x626F0000u, 81u}, // bo -> Tibt
-    {0xA5C10000u, 40u}, // boj -> Latn
-    {0xB1C10000u, 40u}, // bom -> Latn
-    {0xB5C10000u, 40u}, // bon -> Latn
+    {0x99A10000u, 44u}, // bng -> Latn
+    {0xB1A10000u, 44u}, // bnm -> Latn
+    {0xBDA10000u, 44u}, // bnp -> Latn
+    {0x626F0000u, 88u}, // bo -> Tibt
+    {0xA5C10000u, 44u}, // boj -> Latn
+    {0xB1C10000u, 44u}, // bom -> Latn
+    {0xB5C10000u, 44u}, // bon -> Latn
     {0xE1E10000u,  7u}, // bpy -> Beng
-    {0x8A010000u, 40u}, // bqc -> Latn
+    {0x8A010000u, 44u}, // bqc -> Latn
     {0xA2010000u,  1u}, // bqi -> Arab
-    {0xBE010000u, 40u}, // bqp -> Latn
-    {0xD6010000u, 40u}, // bqv -> Latn
-    {0x62720000u, 40u}, // br -> Latn
-    {0x82210000u, 16u}, // bra -> Deva
+    {0xBE010000u, 44u}, // bqp -> Latn
+    {0xD6010000u, 44u}, // bqv -> Latn
+    {0x62720000u, 44u}, // br -> Latn
+    {0x82210000u, 17u}, // bra -> Deva
     {0x9E210000u,  1u}, // brh -> Arab
-    {0xDE210000u, 16u}, // brx -> Deva
-    {0xE6210000u, 40u}, // brz -> Latn
-    {0x62730000u, 40u}, // bs -> Latn
-    {0xA6410000u, 40u}, // bsj -> Latn
+    {0xDE210000u, 17u}, // brx -> Deva
+    {0xE6210000u, 44u}, // brz -> Latn
+    {0x62730000u, 44u}, // bs -> Latn
+    {0xA6410000u, 44u}, // bsj -> Latn
     {0xC2410000u,  6u}, // bsq -> Bass
-    {0xCA410000u, 40u}, // bss -> Latn
-    {0xCE410000u, 18u}, // bst -> Ethi
-    {0xBA610000u, 40u}, // bto -> Latn
-    {0xCE610000u, 40u}, // btt -> Latn
-    {0xD6610000u, 16u}, // btv -> Deva
-    {0x82810000u, 15u}, // bua -> Cyrl
-    {0x8A810000u, 40u}, // buc -> Latn
-    {0x8E810000u, 40u}, // bud -> Latn
-    {0x9A810000u, 40u}, // bug -> Latn
-    {0xAA810000u, 40u}, // buk -> Latn
-    {0xB2810000u, 40u}, // bum -> Latn
-    {0xBA810000u, 40u}, // buo -> Latn
-    {0xCA810000u, 40u}, // bus -> Latn
-    {0xD2810000u, 40u}, // buu -> Latn
-    {0x86A10000u, 40u}, // bvb -> Latn
-    {0x8EC10000u, 40u}, // bwd -> Latn
-    {0xC6C10000u, 40u}, // bwr -> Latn
-    {0x9EE10000u, 40u}, // bxh -> Latn
-    {0x93010000u, 40u}, // bye -> Latn
-    {0xB7010000u, 18u}, // byn -> Ethi
-    {0xC7010000u, 40u}, // byr -> Latn
-    {0xCB010000u, 40u}, // bys -> Latn
-    {0xD7010000u, 40u}, // byv -> Latn
-    {0xDF010000u, 40u}, // byx -> Latn
-    {0x83210000u, 40u}, // bza -> Latn
-    {0x93210000u, 40u}, // bze -> Latn
-    {0x97210000u, 40u}, // bzf -> Latn
-    {0x9F210000u, 40u}, // bzh -> Latn
-    {0xDB210000u, 40u}, // bzw -> Latn
-    {0x63610000u, 40u}, // ca -> Latn
-    {0xB4020000u, 40u}, // can -> Latn
-    {0xA4220000u, 40u}, // cbj -> Latn
-    {0x9C420000u, 40u}, // cch -> Latn
-    {0xBC420000u,  7u}, // ccp -> Beng
-    {0x63650000u, 15u}, // ce -> Cyrl
-    {0x84820000u, 40u}, // ceb -> Latn
-    {0x80A20000u, 40u}, // cfa -> Latn
-    {0x98C20000u, 40u}, // cgg -> Latn
-    {0x63680000u, 40u}, // ch -> Latn
-    {0xA8E20000u, 40u}, // chk -> Latn
-    {0xB0E20000u, 15u}, // chm -> Cyrl
-    {0xB8E20000u, 40u}, // cho -> Latn
-    {0xBCE20000u, 40u}, // chp -> Latn
-    {0xC4E20000u, 12u}, // chr -> Cher
+    {0xCA410000u, 44u}, // bss -> Latn
+    {0xCE410000u, 19u}, // bst -> Ethi
+    {0xBA610000u, 44u}, // bto -> Latn
+    {0xCE610000u, 44u}, // btt -> Latn
+    {0xD6610000u, 17u}, // btv -> Deva
+    {0x82810000u, 16u}, // bua -> Cyrl
+    {0x8A810000u, 44u}, // buc -> Latn
+    {0x8E810000u, 44u}, // bud -> Latn
+    {0x9A810000u, 44u}, // bug -> Latn
+    {0xAA810000u, 44u}, // buk -> Latn
+    {0xB2810000u, 44u}, // bum -> Latn
+    {0xBA810000u, 44u}, // buo -> Latn
+    {0xCA810000u, 44u}, // bus -> Latn
+    {0xD2810000u, 44u}, // buu -> Latn
+    {0x86A10000u, 44u}, // bvb -> Latn
+    {0x8EC10000u, 44u}, // bwd -> Latn
+    {0xC6C10000u, 44u}, // bwr -> Latn
+    {0x9EE10000u, 44u}, // bxh -> Latn
+    {0x93010000u, 44u}, // bye -> Latn
+    {0xB7010000u, 19u}, // byn -> Ethi
+    {0xC7010000u, 44u}, // byr -> Latn
+    {0xCB010000u, 44u}, // bys -> Latn
+    {0xD7010000u, 44u}, // byv -> Latn
+    {0xDF010000u, 44u}, // byx -> Latn
+    {0x83210000u, 44u}, // bza -> Latn
+    {0x93210000u, 44u}, // bze -> Latn
+    {0x97210000u, 44u}, // bzf -> Latn
+    {0x9F210000u, 44u}, // bzh -> Latn
+    {0xDB210000u, 44u}, // bzw -> Latn
+    {0x63610000u, 44u}, // ca -> Latn
+    {0xB4020000u, 44u}, // can -> Latn
+    {0xA4220000u, 44u}, // cbj -> Latn
+    {0x9C420000u, 44u}, // cch -> Latn
+    {0xBC420000u,  9u}, // ccp -> Cakm
+    {0x63650000u, 16u}, // ce -> Cyrl
+    {0x84820000u, 44u}, // ceb -> Latn
+    {0x80A20000u, 44u}, // cfa -> Latn
+    {0x98C20000u, 44u}, // cgg -> Latn
+    {0x63680000u, 44u}, // ch -> Latn
+    {0xA8E20000u, 44u}, // chk -> Latn
+    {0xB0E20000u, 16u}, // chm -> Cyrl
+    {0xB8E20000u, 44u}, // cho -> Latn
+    {0xBCE20000u, 44u}, // chp -> Latn
+    {0xC4E20000u, 13u}, // chr -> Cher
     {0x81220000u,  1u}, // cja -> Arab
-    {0xB1220000u, 11u}, // cjm -> Cham
-    {0xD5220000u, 40u}, // cjv -> Latn
+    {0xB1220000u, 12u}, // cjm -> Cham
+    {0xD5220000u, 44u}, // cjv -> Latn
     {0x85420000u,  1u}, // ckb -> Arab
-    {0xAD420000u, 40u}, // ckl -> Latn
-    {0xB9420000u, 40u}, // cko -> Latn
-    {0xE1420000u, 40u}, // cky -> Latn
-    {0x81620000u, 40u}, // cla -> Latn
-    {0x91820000u, 40u}, // cme -> Latn
-    {0x636F0000u, 40u}, // co -> Latn
-    {0xBDC20000u, 13u}, // cop -> Copt
-    {0xC9E20000u, 40u}, // cps -> Latn
-    {0x63720000u,  9u}, // cr -> Cans
-    {0xA6220000u,  9u}, // crj -> Cans
-    {0xAA220000u,  9u}, // crk -> Cans
-    {0xAE220000u,  9u}, // crl -> Cans
-    {0xB2220000u,  9u}, // crm -> Cans
-    {0xCA220000u, 40u}, // crs -> Latn
-    {0x63730000u, 40u}, // cs -> Latn
-    {0x86420000u, 40u}, // csb -> Latn
-    {0xDA420000u,  9u}, // csw -> Cans
-    {0x8E620000u, 59u}, // ctd -> Pauc
-    {0x63750000u, 15u}, // cu -> Cyrl
-    {0x63760000u, 15u}, // cv -> Cyrl
-    {0x63790000u, 40u}, // cy -> Latn
-    {0x64610000u, 40u}, // da -> Latn
-    {0x8C030000u, 40u}, // dad -> Latn
-    {0x94030000u, 40u}, // daf -> Latn
-    {0x98030000u, 40u}, // dag -> Latn
-    {0x9C030000u, 40u}, // dah -> Latn
-    {0xA8030000u, 40u}, // dak -> Latn
-    {0xC4030000u, 15u}, // dar -> Cyrl
-    {0xD4030000u, 40u}, // dav -> Latn
-    {0x8C230000u, 40u}, // dbd -> Latn
-    {0xC0230000u, 40u}, // dbq -> Latn
+    {0xAD420000u, 44u}, // ckl -> Latn
+    {0xB9420000u, 44u}, // cko -> Latn
+    {0xE1420000u, 44u}, // cky -> Latn
+    {0x81620000u, 44u}, // cla -> Latn
+    {0x91820000u, 44u}, // cme -> Latn
+    {0x99820000u, 77u}, // cmg -> Soyo
+    {0x636F0000u, 44u}, // co -> Latn
+    {0xBDC20000u, 14u}, // cop -> Copt
+    {0xC9E20000u, 44u}, // cps -> Latn
+    {0x63720000u, 10u}, // cr -> Cans
+    {0x9E220000u, 16u}, // crh -> Cyrl
+    {0xA6220000u, 10u}, // crj -> Cans
+    {0xAA220000u, 10u}, // crk -> Cans
+    {0xAE220000u, 10u}, // crl -> Cans
+    {0xB2220000u, 10u}, // crm -> Cans
+    {0xCA220000u, 44u}, // crs -> Latn
+    {0x63730000u, 44u}, // cs -> Latn
+    {0x86420000u, 44u}, // csb -> Latn
+    {0xDA420000u, 10u}, // csw -> Cans
+    {0x8E620000u, 64u}, // ctd -> Pauc
+    {0x63750000u, 16u}, // cu -> Cyrl
+    {0x63760000u, 16u}, // cv -> Cyrl
+    {0x63790000u, 44u}, // cy -> Latn
+    {0x64610000u, 44u}, // da -> Latn
+    {0x8C030000u, 44u}, // dad -> Latn
+    {0x94030000u, 44u}, // daf -> Latn
+    {0x98030000u, 44u}, // dag -> Latn
+    {0x9C030000u, 44u}, // dah -> Latn
+    {0xA8030000u, 44u}, // dak -> Latn
+    {0xC4030000u, 16u}, // dar -> Cyrl
+    {0xD4030000u, 44u}, // dav -> Latn
+    {0x8C230000u, 44u}, // dbd -> Latn
+    {0xC0230000u, 44u}, // dbq -> Latn
     {0x88430000u,  1u}, // dcc -> Arab
-    {0xB4630000u, 40u}, // ddn -> Latn
-    {0x64650000u, 40u}, // de -> Latn
-    {0x8C830000u, 40u}, // ded -> Latn
-    {0xB4830000u, 40u}, // den -> Latn
-    {0x80C30000u, 40u}, // dga -> Latn
-    {0x9CC30000u, 40u}, // dgh -> Latn
-    {0xA0C30000u, 40u}, // dgi -> Latn
+    {0xB4630000u, 44u}, // ddn -> Latn
+    {0x64650000u, 44u}, // de -> Latn
+    {0x8C830000u, 44u}, // ded -> Latn
+    {0xB4830000u, 44u}, // den -> Latn
+    {0x80C30000u, 44u}, // dga -> Latn
+    {0x9CC30000u, 44u}, // dgh -> Latn
+    {0xA0C30000u, 44u}, // dgi -> Latn
     {0xACC30000u,  1u}, // dgl -> Arab
-    {0xC4C30000u, 40u}, // dgr -> Latn
-    {0xE4C30000u, 40u}, // dgz -> Latn
-    {0x81030000u, 40u}, // dia -> Latn
-    {0x91230000u, 40u}, // dje -> Latn
-    {0xA5A30000u, 40u}, // dnj -> Latn
-    {0x85C30000u, 40u}, // dob -> Latn
+    {0xC4C30000u, 44u}, // dgr -> Latn
+    {0xE4C30000u, 44u}, // dgz -> Latn
+    {0x81030000u, 44u}, // dia -> Latn
+    {0x91230000u, 44u}, // dje -> Latn
+    {0xA5A30000u, 44u}, // dnj -> Latn
+    {0x85C30000u, 44u}, // dob -> Latn
     {0xA1C30000u,  1u}, // doi -> Arab
-    {0xBDC30000u, 40u}, // dop -> Latn
-    {0xD9C30000u, 40u}, // dow -> Latn
-    {0xA2230000u, 40u}, // dri -> Latn
-    {0xCA230000u, 18u}, // drs -> Ethi
-    {0x86430000u, 40u}, // dsb -> Latn
-    {0xB2630000u, 40u}, // dtm -> Latn
-    {0xBE630000u, 40u}, // dtp -> Latn
-    {0xCA630000u, 40u}, // dts -> Latn
-    {0xE2630000u, 16u}, // dty -> Deva
-    {0x82830000u, 40u}, // dua -> Latn
-    {0x8A830000u, 40u}, // duc -> Latn
-    {0x8E830000u, 40u}, // dud -> Latn
-    {0x9A830000u, 40u}, // dug -> Latn
-    {0x64760000u, 79u}, // dv -> Thaa
-    {0x82A30000u, 40u}, // dva -> Latn
-    {0xDAC30000u, 40u}, // dww -> Latn
-    {0xBB030000u, 40u}, // dyo -> Latn
-    {0xD3030000u, 40u}, // dyu -> Latn
-    {0x647A0000u, 81u}, // dz -> Tibt
-    {0x9B230000u, 40u}, // dzg -> Latn
-    {0xD0240000u, 40u}, // ebu -> Latn
-    {0x65650000u, 40u}, // ee -> Latn
-    {0xA0A40000u, 40u}, // efi -> Latn
-    {0xACC40000u, 40u}, // egl -> Latn
-    {0xE0C40000u, 17u}, // egy -> Egyp
-    {0xE1440000u, 32u}, // eky -> Kali
-    {0x656C0000u, 21u}, // el -> Grek
-    {0x81840000u, 40u}, // ema -> Latn
-    {0xA1840000u, 40u}, // emi -> Latn
-    {0x656E0000u, 40u}, // en -> Latn
-    {0x656E5841u, 87u}, // en-XA -> ~~~A
-    {0xB5A40000u, 40u}, // enn -> Latn
-    {0xC1A40000u, 40u}, // enq -> Latn
-    {0x656F0000u, 40u}, // eo -> Latn
-    {0xA2240000u, 40u}, // eri -> Latn
-    {0x65730000u, 40u}, // es -> Latn
-    {0xD2440000u, 40u}, // esu -> Latn
-    {0x65740000u, 40u}, // et -> Latn
-    {0xC6640000u, 40u}, // etr -> Latn
-    {0xCE640000u, 30u}, // ett -> Ital
-    {0xD2640000u, 40u}, // etu -> Latn
-    {0xDE640000u, 40u}, // etx -> Latn
-    {0x65750000u, 40u}, // eu -> Latn
-    {0xBAC40000u, 40u}, // ewo -> Latn
-    {0xCEE40000u, 40u}, // ext -> Latn
+    {0xBDC30000u, 44u}, // dop -> Latn
+    {0xD9C30000u, 44u}, // dow -> Latn
+    {0xA2230000u, 44u}, // dri -> Latn
+    {0xCA230000u, 19u}, // drs -> Ethi
+    {0x86430000u, 44u}, // dsb -> Latn
+    {0xB2630000u, 44u}, // dtm -> Latn
+    {0xBE630000u, 44u}, // dtp -> Latn
+    {0xCA630000u, 44u}, // dts -> Latn
+    {0xE2630000u, 17u}, // dty -> Deva
+    {0x82830000u, 44u}, // dua -> Latn
+    {0x8A830000u, 44u}, // duc -> Latn
+    {0x8E830000u, 44u}, // dud -> Latn
+    {0x9A830000u, 44u}, // dug -> Latn
+    {0x64760000u, 86u}, // dv -> Thaa
+    {0x82A30000u, 44u}, // dva -> Latn
+    {0xDAC30000u, 44u}, // dww -> Latn
+    {0xBB030000u, 44u}, // dyo -> Latn
+    {0xD3030000u, 44u}, // dyu -> Latn
+    {0x647A0000u, 88u}, // dz -> Tibt
+    {0x9B230000u, 44u}, // dzg -> Latn
+    {0xD0240000u, 44u}, // ebu -> Latn
+    {0x65650000u, 44u}, // ee -> Latn
+    {0xA0A40000u, 44u}, // efi -> Latn
+    {0xACC40000u, 44u}, // egl -> Latn
+    {0xE0C40000u, 18u}, // egy -> Egyp
+    {0x81440000u, 44u}, // eka -> Latn
+    {0xE1440000u, 36u}, // eky -> Kali
+    {0x656C0000u, 24u}, // el -> Grek
+    {0x81840000u, 44u}, // ema -> Latn
+    {0xA1840000u, 44u}, // emi -> Latn
+    {0x656E0000u, 44u}, // en -> Latn
+    {0x656E5841u, 95u}, // en-XA -> ~~~A
+    {0xB5A40000u, 44u}, // enn -> Latn
+    {0xC1A40000u, 44u}, // enq -> Latn
+    {0x656F0000u, 44u}, // eo -> Latn
+    {0xA2240000u, 44u}, // eri -> Latn
+    {0x65730000u, 44u}, // es -> Latn
+    {0x9A440000u, 22u}, // esg -> Gonm
+    {0xD2440000u, 44u}, // esu -> Latn
+    {0x65740000u, 44u}, // et -> Latn
+    {0xC6640000u, 44u}, // etr -> Latn
+    {0xCE640000u, 34u}, // ett -> Ital
+    {0xD2640000u, 44u}, // etu -> Latn
+    {0xDE640000u, 44u}, // etx -> Latn
+    {0x65750000u, 44u}, // eu -> Latn
+    {0xBAC40000u, 44u}, // ewo -> Latn
+    {0xCEE40000u, 44u}, // ext -> Latn
     {0x66610000u,  1u}, // fa -> Arab
-    {0x80050000u, 40u}, // faa -> Latn
-    {0x84050000u, 40u}, // fab -> Latn
-    {0x98050000u, 40u}, // fag -> Latn
-    {0xA0050000u, 40u}, // fai -> Latn
-    {0xB4050000u, 40u}, // fan -> Latn
-    {0x66660000u, 40u}, // ff -> Latn
-    {0xA0A50000u, 40u}, // ffi -> Latn
-    {0xB0A50000u, 40u}, // ffm -> Latn
-    {0x66690000u, 40u}, // fi -> Latn
+    {0x80050000u, 44u}, // faa -> Latn
+    {0x84050000u, 44u}, // fab -> Latn
+    {0x98050000u, 44u}, // fag -> Latn
+    {0xA0050000u, 44u}, // fai -> Latn
+    {0xB4050000u, 44u}, // fan -> Latn
+    {0x66660000u, 44u}, // ff -> Latn
+    {0xA0A50000u, 44u}, // ffi -> Latn
+    {0xB0A50000u, 44u}, // ffm -> Latn
+    {0x66690000u, 44u}, // fi -> Latn
     {0x81050000u,  1u}, // fia -> Arab
-    {0xAD050000u, 40u}, // fil -> Latn
-    {0xCD050000u, 40u}, // fit -> Latn
-    {0x666A0000u, 40u}, // fj -> Latn
-    {0xC5650000u, 40u}, // flr -> Latn
-    {0xBD850000u, 40u}, // fmp -> Latn
-    {0x666F0000u, 40u}, // fo -> Latn
-    {0x8DC50000u, 40u}, // fod -> Latn
-    {0xB5C50000u, 40u}, // fon -> Latn
-    {0xC5C50000u, 40u}, // for -> Latn
-    {0x91E50000u, 40u}, // fpe -> Latn
-    {0xCA050000u, 40u}, // fqs -> Latn
-    {0x66720000u, 40u}, // fr -> Latn
-    {0x8A250000u, 40u}, // frc -> Latn
-    {0xBE250000u, 40u}, // frp -> Latn
-    {0xC6250000u, 40u}, // frr -> Latn
-    {0xCA250000u, 40u}, // frs -> Latn
+    {0xAD050000u, 44u}, // fil -> Latn
+    {0xCD050000u, 44u}, // fit -> Latn
+    {0x666A0000u, 44u}, // fj -> Latn
+    {0xC5650000u, 44u}, // flr -> Latn
+    {0xBD850000u, 44u}, // fmp -> Latn
+    {0x666F0000u, 44u}, // fo -> Latn
+    {0x8DC50000u, 44u}, // fod -> Latn
+    {0xB5C50000u, 44u}, // fon -> Latn
+    {0xC5C50000u, 44u}, // for -> Latn
+    {0x91E50000u, 44u}, // fpe -> Latn
+    {0xCA050000u, 44u}, // fqs -> Latn
+    {0x66720000u, 44u}, // fr -> Latn
+    {0x8A250000u, 44u}, // frc -> Latn
+    {0xBE250000u, 44u}, // frp -> Latn
+    {0xC6250000u, 44u}, // frr -> Latn
+    {0xCA250000u, 44u}, // frs -> Latn
     {0x86850000u,  1u}, // fub -> Arab
-    {0x8E850000u, 40u}, // fud -> Latn
-    {0x92850000u, 40u}, // fue -> Latn
-    {0x96850000u, 40u}, // fuf -> Latn
-    {0x9E850000u, 40u}, // fuh -> Latn
-    {0xC2850000u, 40u}, // fuq -> Latn
-    {0xC6850000u, 40u}, // fur -> Latn
-    {0xD6850000u, 40u}, // fuv -> Latn
-    {0xE2850000u, 40u}, // fuy -> Latn
-    {0xC6A50000u, 40u}, // fvr -> Latn
-    {0x66790000u, 40u}, // fy -> Latn
-    {0x67610000u, 40u}, // ga -> Latn
-    {0x80060000u, 40u}, // gaa -> Latn
-    {0x94060000u, 40u}, // gaf -> Latn
-    {0x98060000u, 40u}, // gag -> Latn
-    {0x9C060000u, 40u}, // gah -> Latn
-    {0xA4060000u, 40u}, // gaj -> Latn
-    {0xB0060000u, 40u}, // gam -> Latn
-    {0xB4060000u, 24u}, // gan -> Hans
-    {0xD8060000u, 40u}, // gaw -> Latn
-    {0xE0060000u, 40u}, // gay -> Latn
-    {0x94260000u, 40u}, // gbf -> Latn
-    {0xB0260000u, 16u}, // gbm -> Deva
-    {0xE0260000u, 40u}, // gby -> Latn
+    {0x8E850000u, 44u}, // fud -> Latn
+    {0x92850000u, 44u}, // fue -> Latn
+    {0x96850000u, 44u}, // fuf -> Latn
+    {0x9E850000u, 44u}, // fuh -> Latn
+    {0xC2850000u, 44u}, // fuq -> Latn
+    {0xC6850000u, 44u}, // fur -> Latn
+    {0xD6850000u, 44u}, // fuv -> Latn
+    {0xE2850000u, 44u}, // fuy -> Latn
+    {0xC6A50000u, 44u}, // fvr -> Latn
+    {0x66790000u, 44u}, // fy -> Latn
+    {0x67610000u, 44u}, // ga -> Latn
+    {0x80060000u, 44u}, // gaa -> Latn
+    {0x94060000u, 44u}, // gaf -> Latn
+    {0x98060000u, 44u}, // gag -> Latn
+    {0x9C060000u, 44u}, // gah -> Latn
+    {0xA4060000u, 44u}, // gaj -> Latn
+    {0xB0060000u, 44u}, // gam -> Latn
+    {0xB4060000u, 27u}, // gan -> Hans
+    {0xD8060000u, 44u}, // gaw -> Latn
+    {0xE0060000u, 44u}, // gay -> Latn
+    {0x80260000u, 44u}, // gba -> Latn
+    {0x94260000u, 44u}, // gbf -> Latn
+    {0xB0260000u, 17u}, // gbm -> Deva
+    {0xE0260000u, 44u}, // gby -> Latn
     {0xE4260000u,  1u}, // gbz -> Arab
-    {0xC4460000u, 40u}, // gcr -> Latn
-    {0x67640000u, 40u}, // gd -> Latn
-    {0x90660000u, 40u}, // gde -> Latn
-    {0xB4660000u, 40u}, // gdn -> Latn
-    {0xC4660000u, 40u}, // gdr -> Latn
-    {0x84860000u, 40u}, // geb -> Latn
-    {0xA4860000u, 40u}, // gej -> Latn
-    {0xAC860000u, 40u}, // gel -> Latn
-    {0xE4860000u, 18u}, // gez -> Ethi
-    {0xA8A60000u, 40u}, // gfk -> Latn
-    {0xB4C60000u, 16u}, // ggn -> Deva
-    {0xC8E60000u, 40u}, // ghs -> Latn
-    {0xAD060000u, 40u}, // gil -> Latn
-    {0xB1060000u, 40u}, // gim -> Latn
+    {0xC4460000u, 44u}, // gcr -> Latn
+    {0x67640000u, 44u}, // gd -> Latn
+    {0x90660000u, 44u}, // gde -> Latn
+    {0xB4660000u, 44u}, // gdn -> Latn
+    {0xC4660000u, 44u}, // gdr -> Latn
+    {0x84860000u, 44u}, // geb -> Latn
+    {0xA4860000u, 44u}, // gej -> Latn
+    {0xAC860000u, 44u}, // gel -> Latn
+    {0xE4860000u, 19u}, // gez -> Ethi
+    {0xA8A60000u, 44u}, // gfk -> Latn
+    {0xB4C60000u, 17u}, // ggn -> Deva
+    {0xC8E60000u, 44u}, // ghs -> Latn
+    {0xAD060000u, 44u}, // gil -> Latn
+    {0xB1060000u, 44u}, // gim -> Latn
     {0xA9260000u,  1u}, // gjk -> Arab
-    {0xB5260000u, 40u}, // gjn -> Latn
+    {0xB5260000u, 44u}, // gjn -> Latn
     {0xD1260000u,  1u}, // gju -> Arab
-    {0xB5460000u, 40u}, // gkn -> Latn
-    {0xBD460000u, 40u}, // gkp -> Latn
-    {0x676C0000u, 40u}, // gl -> Latn
+    {0xB5460000u, 44u}, // gkn -> Latn
+    {0xBD460000u, 44u}, // gkp -> Latn
+    {0x676C0000u, 44u}, // gl -> Latn
     {0xA9660000u,  1u}, // glk -> Arab
-    {0xB1860000u, 40u}, // gmm -> Latn
-    {0xD5860000u, 18u}, // gmv -> Ethi
-    {0x676E0000u, 40u}, // gn -> Latn
-    {0x8DA60000u, 40u}, // gnd -> Latn
-    {0x99A60000u, 40u}, // gng -> Latn
-    {0x8DC60000u, 40u}, // god -> Latn
-    {0x95C60000u, 18u}, // gof -> Ethi
-    {0xA1C60000u, 40u}, // goi -> Latn
-    {0xB1C60000u, 16u}, // gom -> Deva
-    {0xB5C60000u, 77u}, // gon -> Telu
-    {0xC5C60000u, 40u}, // gor -> Latn
-    {0xC9C60000u, 40u}, // gos -> Latn
-    {0xCDC60000u, 20u}, // got -> Goth
-    {0x8A260000u, 14u}, // grc -> Cprt
+    {0xB1860000u, 44u}, // gmm -> Latn
+    {0xD5860000u, 19u}, // gmv -> Ethi
+    {0x676E0000u, 44u}, // gn -> Latn
+    {0x8DA60000u, 44u}, // gnd -> Latn
+    {0x99A60000u, 44u}, // gng -> Latn
+    {0x8DC60000u, 44u}, // god -> Latn
+    {0x95C60000u, 19u}, // gof -> Ethi
+    {0xA1C60000u, 44u}, // goi -> Latn
+    {0xB1C60000u, 17u}, // gom -> Deva
+    {0xB5C60000u, 84u}, // gon -> Telu
+    {0xC5C60000u, 44u}, // gor -> Latn
+    {0xC9C60000u, 44u}, // gos -> Latn
+    {0xCDC60000u, 23u}, // got -> Goth
+    {0x86260000u, 44u}, // grb -> Latn
+    {0x8A260000u, 15u}, // grc -> Cprt
     {0xCE260000u,  7u}, // grt -> Beng
-    {0xDA260000u, 40u}, // grw -> Latn
-    {0xDA460000u, 40u}, // gsw -> Latn
-    {0x67750000u, 22u}, // gu -> Gujr
-    {0x86860000u, 40u}, // gub -> Latn
-    {0x8A860000u, 40u}, // guc -> Latn
-    {0x8E860000u, 40u}, // gud -> Latn
-    {0xC6860000u, 40u}, // gur -> Latn
-    {0xDA860000u, 40u}, // guw -> Latn
-    {0xDE860000u, 40u}, // gux -> Latn
-    {0xE6860000u, 40u}, // guz -> Latn
-    {0x67760000u, 40u}, // gv -> Latn
-    {0x96A60000u, 40u}, // gvf -> Latn
-    {0xC6A60000u, 16u}, // gvr -> Deva
-    {0xCAA60000u, 40u}, // gvs -> Latn
+    {0xDA260000u, 44u}, // grw -> Latn
+    {0xDA460000u, 44u}, // gsw -> Latn
+    {0x67750000u, 25u}, // gu -> Gujr
+    {0x86860000u, 44u}, // gub -> Latn
+    {0x8A860000u, 44u}, // guc -> Latn
+    {0x8E860000u, 44u}, // gud -> Latn
+    {0xC6860000u, 44u}, // gur -> Latn
+    {0xDA860000u, 44u}, // guw -> Latn
+    {0xDE860000u, 44u}, // gux -> Latn
+    {0xE6860000u, 44u}, // guz -> Latn
+    {0x67760000u, 44u}, // gv -> Latn
+    {0x96A60000u, 44u}, // gvf -> Latn
+    {0xC6A60000u, 17u}, // gvr -> Deva
+    {0xCAA60000u, 44u}, // gvs -> Latn
     {0x8AC60000u,  1u}, // gwc -> Arab
-    {0xA2C60000u, 40u}, // gwi -> Latn
+    {0xA2C60000u, 44u}, // gwi -> Latn
     {0xCEC60000u,  1u}, // gwt -> Arab
-    {0xA3060000u, 40u}, // gyi -> Latn
-    {0x68610000u, 40u}, // ha -> Latn
+    {0xA3060000u, 44u}, // gyi -> Latn
+    {0x68610000u, 44u}, // ha -> Latn
     {0x6861434Du,  1u}, // ha-CM -> Arab
     {0x68615344u,  1u}, // ha-SD -> Arab
-    {0x98070000u, 40u}, // hag -> Latn
-    {0xA8070000u, 24u}, // hak -> Hans
-    {0xB0070000u, 40u}, // ham -> Latn
-    {0xD8070000u, 40u}, // haw -> Latn
+    {0x98070000u, 44u}, // hag -> Latn
+    {0xA8070000u, 27u}, // hak -> Hans
+    {0xB0070000u, 44u}, // ham -> Latn
+    {0xD8070000u, 44u}, // haw -> Latn
     {0xE4070000u,  1u}, // haz -> Arab
-    {0x84270000u, 40u}, // hbb -> Latn
-    {0xE0670000u, 18u}, // hdy -> Ethi
-    {0x68650000u, 27u}, // he -> Hebr
-    {0xE0E70000u, 40u}, // hhy -> Latn
-    {0x68690000u, 16u}, // hi -> Deva
-    {0x81070000u, 40u}, // hia -> Latn
-    {0x95070000u, 40u}, // hif -> Latn
-    {0x99070000u, 40u}, // hig -> Latn
-    {0x9D070000u, 40u}, // hih -> Latn
-    {0xAD070000u, 40u}, // hil -> Latn
-    {0x81670000u, 40u}, // hla -> Latn
-    {0xD1670000u, 28u}, // hlu -> Hluw
-    {0x8D870000u, 62u}, // hmd -> Plrd
-    {0xCD870000u, 40u}, // hmt -> Latn
+    {0x84270000u, 44u}, // hbb -> Latn
+    {0xE0670000u, 19u}, // hdy -> Ethi
+    {0x68650000u, 30u}, // he -> Hebr
+    {0xE0E70000u, 44u}, // hhy -> Latn
+    {0x68690000u, 17u}, // hi -> Deva
+    {0x81070000u, 44u}, // hia -> Latn
+    {0x95070000u, 44u}, // hif -> Latn
+    {0x99070000u, 44u}, // hig -> Latn
+    {0x9D070000u, 44u}, // hih -> Latn
+    {0xAD070000u, 44u}, // hil -> Latn
+    {0x81670000u, 44u}, // hla -> Latn
+    {0xD1670000u, 31u}, // hlu -> Hluw
+    {0x8D870000u, 67u}, // hmd -> Plrd
+    {0xCD870000u, 44u}, // hmt -> Latn
     {0x8DA70000u,  1u}, // hnd -> Arab
-    {0x91A70000u, 16u}, // hne -> Deva
-    {0xA5A70000u, 29u}, // hnj -> Hmng
-    {0xB5A70000u, 40u}, // hnn -> Latn
+    {0x91A70000u, 17u}, // hne -> Deva
+    {0xA5A70000u, 32u}, // hnj -> Hmng
+    {0xB5A70000u, 44u}, // hnn -> Latn
     {0xB9A70000u,  1u}, // hno -> Arab
-    {0x686F0000u, 40u}, // ho -> Latn
-    {0x89C70000u, 16u}, // hoc -> Deva
-    {0xA5C70000u, 16u}, // hoj -> Deva
-    {0xCDC70000u, 40u}, // hot -> Latn
-    {0x68720000u, 40u}, // hr -> Latn
-    {0x86470000u, 40u}, // hsb -> Latn
-    {0xB6470000u, 24u}, // hsn -> Hans
-    {0x68740000u, 40u}, // ht -> Latn
-    {0x68750000u, 40u}, // hu -> Latn
-    {0xA2870000u, 40u}, // hui -> Latn
+    {0x686F0000u, 44u}, // ho -> Latn
+    {0x89C70000u, 17u}, // hoc -> Deva
+    {0xA5C70000u, 17u}, // hoj -> Deva
+    {0xCDC70000u, 44u}, // hot -> Latn
+    {0x68720000u, 44u}, // hr -> Latn
+    {0x86470000u, 44u}, // hsb -> Latn
+    {0xB6470000u, 27u}, // hsn -> Hans
+    {0x68740000u, 44u}, // ht -> Latn
+    {0x68750000u, 44u}, // hu -> Latn
+    {0xA2870000u, 44u}, // hui -> Latn
     {0x68790000u,  3u}, // hy -> Armn
-    {0x687A0000u, 40u}, // hz -> Latn
-    {0x69610000u, 40u}, // ia -> Latn
-    {0xB4080000u, 40u}, // ian -> Latn
-    {0xC4080000u, 40u}, // iar -> Latn
-    {0x80280000u, 40u}, // iba -> Latn
-    {0x84280000u, 40u}, // ibb -> Latn
-    {0xE0280000u, 40u}, // iby -> Latn
-    {0x80480000u, 40u}, // ica -> Latn
-    {0x9C480000u, 40u}, // ich -> Latn
-    {0x69640000u, 40u}, // id -> Latn
-    {0x8C680000u, 40u}, // idd -> Latn
-    {0xA0680000u, 40u}, // idi -> Latn
-    {0xD0680000u, 40u}, // idu -> Latn
-    {0x69670000u, 40u}, // ig -> Latn
-    {0x84C80000u, 40u}, // igb -> Latn
-    {0x90C80000u, 40u}, // ige -> Latn
-    {0x69690000u, 86u}, // ii -> Yiii
-    {0xA5280000u, 40u}, // ijj -> Latn
-    {0x696B0000u, 40u}, // ik -> Latn
-    {0xA9480000u, 40u}, // ikk -> Latn
-    {0xCD480000u, 40u}, // ikt -> Latn
-    {0xD9480000u, 40u}, // ikw -> Latn
-    {0xDD480000u, 40u}, // ikx -> Latn
-    {0xB9680000u, 40u}, // ilo -> Latn
-    {0xB9880000u, 40u}, // imo -> Latn
-    {0x696E0000u, 40u}, // in -> Latn
-    {0x9DA80000u, 15u}, // inh -> Cyrl
-    {0xD1C80000u, 40u}, // iou -> Latn
-    {0xA2280000u, 40u}, // iri -> Latn
-    {0x69730000u, 40u}, // is -> Latn
-    {0x69740000u, 40u}, // it -> Latn
-    {0x69750000u,  9u}, // iu -> Cans
-    {0x69770000u, 27u}, // iw -> Hebr
-    {0xB2C80000u, 40u}, // iwm -> Latn
-    {0xCAC80000u, 40u}, // iws -> Latn
-    {0x9F280000u, 40u}, // izh -> Latn
-    {0xA3280000u, 40u}, // izi -> Latn
-    {0x6A610000u, 31u}, // ja -> Jpan
-    {0x84090000u, 40u}, // jab -> Latn
-    {0xB0090000u, 40u}, // jam -> Latn
-    {0xD0290000u, 40u}, // jbu -> Latn
-    {0xB4890000u, 40u}, // jen -> Latn
-    {0xA8C90000u, 40u}, // jgk -> Latn
-    {0xB8C90000u, 40u}, // jgo -> Latn
-    {0x6A690000u, 27u}, // ji -> Hebr
-    {0x85090000u, 40u}, // jib -> Latn
-    {0x89890000u, 40u}, // jmc -> Latn
-    {0xAD890000u, 16u}, // jml -> Deva
-    {0x82290000u, 40u}, // jra -> Latn
-    {0xCE890000u, 40u}, // jut -> Latn
-    {0x6A760000u, 40u}, // jv -> Latn
-    {0x6A770000u, 40u}, // jw -> Latn
-    {0x6B610000u, 19u}, // ka -> Geor
-    {0x800A0000u, 15u}, // kaa -> Cyrl
-    {0x840A0000u, 40u}, // kab -> Latn
-    {0x880A0000u, 40u}, // kac -> Latn
-    {0x8C0A0000u, 40u}, // kad -> Latn
-    {0xA00A0000u, 40u}, // kai -> Latn
-    {0xA40A0000u, 40u}, // kaj -> Latn
-    {0xB00A0000u, 40u}, // kam -> Latn
-    {0xB80A0000u, 40u}, // kao -> Latn
-    {0x8C2A0000u, 15u}, // kbd -> Cyrl
-    {0xB02A0000u, 40u}, // kbm -> Latn
-    {0xBC2A0000u, 40u}, // kbp -> Latn
-    {0xC02A0000u, 40u}, // kbq -> Latn
-    {0xDC2A0000u, 40u}, // kbx -> Latn
+    {0x687A0000u, 44u}, // hz -> Latn
+    {0x69610000u, 44u}, // ia -> Latn
+    {0xB4080000u, 44u}, // ian -> Latn
+    {0xC4080000u, 44u}, // iar -> Latn
+    {0x80280000u, 44u}, // iba -> Latn
+    {0x84280000u, 44u}, // ibb -> Latn
+    {0xE0280000u, 44u}, // iby -> Latn
+    {0x80480000u, 44u}, // ica -> Latn
+    {0x9C480000u, 44u}, // ich -> Latn
+    {0x69640000u, 44u}, // id -> Latn
+    {0x8C680000u, 44u}, // idd -> Latn
+    {0xA0680000u, 44u}, // idi -> Latn
+    {0xD0680000u, 44u}, // idu -> Latn
+    {0x90A80000u, 44u}, // ife -> Latn
+    {0x69670000u, 44u}, // ig -> Latn
+    {0x84C80000u, 44u}, // igb -> Latn
+    {0x90C80000u, 44u}, // ige -> Latn
+    {0x69690000u, 94u}, // ii -> Yiii
+    {0xA5280000u, 44u}, // ijj -> Latn
+    {0x696B0000u, 44u}, // ik -> Latn
+    {0xA9480000u, 44u}, // ikk -> Latn
+    {0xCD480000u, 44u}, // ikt -> Latn
+    {0xD9480000u, 44u}, // ikw -> Latn
+    {0xDD480000u, 44u}, // ikx -> Latn
+    {0xB9680000u, 44u}, // ilo -> Latn
+    {0xB9880000u, 44u}, // imo -> Latn
+    {0x696E0000u, 44u}, // in -> Latn
+    {0x9DA80000u, 16u}, // inh -> Cyrl
+    {0x696F0000u, 44u}, // io -> Latn
+    {0xD1C80000u, 44u}, // iou -> Latn
+    {0xA2280000u, 44u}, // iri -> Latn
+    {0x69730000u, 44u}, // is -> Latn
+    {0x69740000u, 44u}, // it -> Latn
+    {0x69750000u, 10u}, // iu -> Cans
+    {0x69770000u, 30u}, // iw -> Hebr
+    {0xB2C80000u, 44u}, // iwm -> Latn
+    {0xCAC80000u, 44u}, // iws -> Latn
+    {0x9F280000u, 44u}, // izh -> Latn
+    {0xA3280000u, 44u}, // izi -> Latn
+    {0x6A610000u, 35u}, // ja -> Jpan
+    {0x84090000u, 44u}, // jab -> Latn
+    {0xB0090000u, 44u}, // jam -> Latn
+    {0xB8290000u, 44u}, // jbo -> Latn
+    {0xD0290000u, 44u}, // jbu -> Latn
+    {0xB4890000u, 44u}, // jen -> Latn
+    {0xA8C90000u, 44u}, // jgk -> Latn
+    {0xB8C90000u, 44u}, // jgo -> Latn
+    {0x6A690000u, 30u}, // ji -> Hebr
+    {0x85090000u, 44u}, // jib -> Latn
+    {0x89890000u, 44u}, // jmc -> Latn
+    {0xAD890000u, 17u}, // jml -> Deva
+    {0x82290000u, 44u}, // jra -> Latn
+    {0xCE890000u, 44u}, // jut -> Latn
+    {0x6A760000u, 44u}, // jv -> Latn
+    {0x6A770000u, 44u}, // jw -> Latn
+    {0x6B610000u, 20u}, // ka -> Geor
+    {0x800A0000u, 16u}, // kaa -> Cyrl
+    {0x840A0000u, 44u}, // kab -> Latn
+    {0x880A0000u, 44u}, // kac -> Latn
+    {0x8C0A0000u, 44u}, // kad -> Latn
+    {0xA00A0000u, 44u}, // kai -> Latn
+    {0xA40A0000u, 44u}, // kaj -> Latn
+    {0xB00A0000u, 44u}, // kam -> Latn
+    {0xB80A0000u, 44u}, // kao -> Latn
+    {0x8C2A0000u, 16u}, // kbd -> Cyrl
+    {0xB02A0000u, 44u}, // kbm -> Latn
+    {0xBC2A0000u, 44u}, // kbp -> Latn
+    {0xC02A0000u, 44u}, // kbq -> Latn
+    {0xDC2A0000u, 44u}, // kbx -> Latn
     {0xE02A0000u,  1u}, // kby -> Arab
-    {0x984A0000u, 40u}, // kcg -> Latn
-    {0xA84A0000u, 40u}, // kck -> Latn
-    {0xAC4A0000u, 40u}, // kcl -> Latn
-    {0xCC4A0000u, 40u}, // kct -> Latn
-    {0x906A0000u, 40u}, // kde -> Latn
+    {0x984A0000u, 44u}, // kcg -> Latn
+    {0xA84A0000u, 44u}, // kck -> Latn
+    {0xAC4A0000u, 44u}, // kcl -> Latn
+    {0xCC4A0000u, 44u}, // kct -> Latn
+    {0x906A0000u, 44u}, // kde -> Latn
     {0x9C6A0000u,  1u}, // kdh -> Arab
-    {0xAC6A0000u, 40u}, // kdl -> Latn
-    {0xCC6A0000u, 80u}, // kdt -> Thai
-    {0x808A0000u, 40u}, // kea -> Latn
-    {0xB48A0000u, 40u}, // ken -> Latn
-    {0xE48A0000u, 40u}, // kez -> Latn
-    {0xB8AA0000u, 40u}, // kfo -> Latn
-    {0xC4AA0000u, 16u}, // kfr -> Deva
-    {0xE0AA0000u, 16u}, // kfy -> Deva
-    {0x6B670000u, 40u}, // kg -> Latn
-    {0x90CA0000u, 40u}, // kge -> Latn
-    {0x94CA0000u, 40u}, // kgf -> Latn
-    {0xBCCA0000u, 40u}, // kgp -> Latn
-    {0x80EA0000u, 40u}, // kha -> Latn
-    {0x84EA0000u, 73u}, // khb -> Talu
-    {0xB4EA0000u, 16u}, // khn -> Deva
-    {0xC0EA0000u, 40u}, // khq -> Latn
-    {0xC8EA0000u, 40u}, // khs -> Latn
-    {0xCCEA0000u, 52u}, // kht -> Mymr
+    {0xAC6A0000u, 44u}, // kdl -> Latn
+    {0xCC6A0000u, 87u}, // kdt -> Thai
+    {0x808A0000u, 44u}, // kea -> Latn
+    {0xB48A0000u, 44u}, // ken -> Latn
+    {0xE48A0000u, 44u}, // kez -> Latn
+    {0xB8AA0000u, 44u}, // kfo -> Latn
+    {0xC4AA0000u, 17u}, // kfr -> Deva
+    {0xE0AA0000u, 17u}, // kfy -> Deva
+    {0x6B670000u, 44u}, // kg -> Latn
+    {0x90CA0000u, 44u}, // kge -> Latn
+    {0x94CA0000u, 44u}, // kgf -> Latn
+    {0xBCCA0000u, 44u}, // kgp -> Latn
+    {0x80EA0000u, 44u}, // kha -> Latn
+    {0x84EA0000u, 80u}, // khb -> Talu
+    {0xB4EA0000u, 17u}, // khn -> Deva
+    {0xC0EA0000u, 44u}, // khq -> Latn
+    {0xC8EA0000u, 44u}, // khs -> Latn
+    {0xCCEA0000u, 56u}, // kht -> Mymr
     {0xD8EA0000u,  1u}, // khw -> Arab
-    {0xE4EA0000u, 40u}, // khz -> Latn
-    {0x6B690000u, 40u}, // ki -> Latn
-    {0xA50A0000u, 40u}, // kij -> Latn
-    {0xD10A0000u, 40u}, // kiu -> Latn
-    {0xD90A0000u, 40u}, // kiw -> Latn
-    {0x6B6A0000u, 40u}, // kj -> Latn
-    {0x8D2A0000u, 40u}, // kjd -> Latn
-    {0x992A0000u, 39u}, // kjg -> Laoo
-    {0xC92A0000u, 40u}, // kjs -> Latn
-    {0xE12A0000u, 40u}, // kjy -> Latn
-    {0x6B6B0000u, 15u}, // kk -> Cyrl
+    {0xE4EA0000u, 44u}, // khz -> Latn
+    {0x6B690000u, 44u}, // ki -> Latn
+    {0xA50A0000u, 44u}, // kij -> Latn
+    {0xD10A0000u, 44u}, // kiu -> Latn
+    {0xD90A0000u, 44u}, // kiw -> Latn
+    {0x6B6A0000u, 44u}, // kj -> Latn
+    {0x8D2A0000u, 44u}, // kjd -> Latn
+    {0x992A0000u, 43u}, // kjg -> Laoo
+    {0xC92A0000u, 44u}, // kjs -> Latn
+    {0xE12A0000u, 44u}, // kjy -> Latn
+    {0x6B6B0000u, 16u}, // kk -> Cyrl
     {0x6B6B4146u,  1u}, // kk-AF -> Arab
     {0x6B6B434Eu,  1u}, // kk-CN -> Arab
     {0x6B6B4952u,  1u}, // kk-IR -> Arab
     {0x6B6B4D4Eu,  1u}, // kk-MN -> Arab
-    {0x894A0000u, 40u}, // kkc -> Latn
-    {0xA54A0000u, 40u}, // kkj -> Latn
-    {0x6B6C0000u, 40u}, // kl -> Latn
-    {0xB56A0000u, 40u}, // kln -> Latn
-    {0xC16A0000u, 40u}, // klq -> Latn
-    {0xCD6A0000u, 40u}, // klt -> Latn
-    {0xDD6A0000u, 40u}, // klx -> Latn
-    {0x6B6D0000u, 35u}, // km -> Khmr
-    {0x858A0000u, 40u}, // kmb -> Latn
-    {0x9D8A0000u, 40u}, // kmh -> Latn
-    {0xB98A0000u, 40u}, // kmo -> Latn
-    {0xC98A0000u, 40u}, // kms -> Latn
-    {0xD18A0000u, 40u}, // kmu -> Latn
-    {0xD98A0000u, 40u}, // kmw -> Latn
-    {0x6B6E0000u, 36u}, // kn -> Knda
-    {0xBDAA0000u, 40u}, // knp -> Latn
-    {0x6B6F0000u, 37u}, // ko -> Kore
-    {0xA1CA0000u, 15u}, // koi -> Cyrl
-    {0xA9CA0000u, 16u}, // kok -> Deva
-    {0xADCA0000u, 40u}, // kol -> Latn
-    {0xC9CA0000u, 40u}, // kos -> Latn
-    {0xE5CA0000u, 40u}, // koz -> Latn
-    {0x91EA0000u, 40u}, // kpe -> Latn
-    {0x95EA0000u, 40u}, // kpf -> Latn
-    {0xB9EA0000u, 40u}, // kpo -> Latn
-    {0xC5EA0000u, 40u}, // kpr -> Latn
-    {0xDDEA0000u, 40u}, // kpx -> Latn
-    {0x860A0000u, 40u}, // kqb -> Latn
-    {0x960A0000u, 40u}, // kqf -> Latn
-    {0xCA0A0000u, 40u}, // kqs -> Latn
-    {0xE20A0000u, 18u}, // kqy -> Ethi
-    {0x8A2A0000u, 15u}, // krc -> Cyrl
-    {0xA22A0000u, 40u}, // kri -> Latn
-    {0xA62A0000u, 40u}, // krj -> Latn
-    {0xAE2A0000u, 40u}, // krl -> Latn
-    {0xCA2A0000u, 40u}, // krs -> Latn
-    {0xD22A0000u, 16u}, // kru -> Deva
+    {0x894A0000u, 44u}, // kkc -> Latn
+    {0xA54A0000u, 44u}, // kkj -> Latn
+    {0x6B6C0000u, 44u}, // kl -> Latn
+    {0xB56A0000u, 44u}, // kln -> Latn
+    {0xC16A0000u, 44u}, // klq -> Latn
+    {0xCD6A0000u, 44u}, // klt -> Latn
+    {0xDD6A0000u, 44u}, // klx -> Latn
+    {0x6B6D0000u, 39u}, // km -> Khmr
+    {0x858A0000u, 44u}, // kmb -> Latn
+    {0x9D8A0000u, 44u}, // kmh -> Latn
+    {0xB98A0000u, 44u}, // kmo -> Latn
+    {0xC98A0000u, 44u}, // kms -> Latn
+    {0xD18A0000u, 44u}, // kmu -> Latn
+    {0xD98A0000u, 44u}, // kmw -> Latn
+    {0x6B6E0000u, 40u}, // kn -> Knda
+    {0x95AA0000u, 44u}, // knf -> Latn
+    {0xBDAA0000u, 44u}, // knp -> Latn
+    {0x6B6F0000u, 41u}, // ko -> Kore
+    {0xA1CA0000u, 16u}, // koi -> Cyrl
+    {0xA9CA0000u, 17u}, // kok -> Deva
+    {0xADCA0000u, 44u}, // kol -> Latn
+    {0xC9CA0000u, 44u}, // kos -> Latn
+    {0xE5CA0000u, 44u}, // koz -> Latn
+    {0x91EA0000u, 44u}, // kpe -> Latn
+    {0x95EA0000u, 44u}, // kpf -> Latn
+    {0xB9EA0000u, 44u}, // kpo -> Latn
+    {0xC5EA0000u, 44u}, // kpr -> Latn
+    {0xDDEA0000u, 44u}, // kpx -> Latn
+    {0x860A0000u, 44u}, // kqb -> Latn
+    {0x960A0000u, 44u}, // kqf -> Latn
+    {0xCA0A0000u, 44u}, // kqs -> Latn
+    {0xE20A0000u, 19u}, // kqy -> Ethi
+    {0x6B720000u, 44u}, // kr -> Latn
+    {0x8A2A0000u, 16u}, // krc -> Cyrl
+    {0xA22A0000u, 44u}, // kri -> Latn
+    {0xA62A0000u, 44u}, // krj -> Latn
+    {0xAE2A0000u, 44u}, // krl -> Latn
+    {0xCA2A0000u, 44u}, // krs -> Latn
+    {0xD22A0000u, 17u}, // kru -> Deva
     {0x6B730000u,  1u}, // ks -> Arab
-    {0x864A0000u, 40u}, // ksb -> Latn
-    {0x8E4A0000u, 40u}, // ksd -> Latn
-    {0x964A0000u, 40u}, // ksf -> Latn
-    {0x9E4A0000u, 40u}, // ksh -> Latn
-    {0xA64A0000u, 40u}, // ksj -> Latn
-    {0xC64A0000u, 40u}, // ksr -> Latn
-    {0x866A0000u, 18u}, // ktb -> Ethi
-    {0xB26A0000u, 40u}, // ktm -> Latn
-    {0xBA6A0000u, 40u}, // kto -> Latn
-    {0x6B750000u, 40u}, // ku -> Latn
+    {0x864A0000u, 44u}, // ksb -> Latn
+    {0x8E4A0000u, 44u}, // ksd -> Latn
+    {0x964A0000u, 44u}, // ksf -> Latn
+    {0x9E4A0000u, 44u}, // ksh -> Latn
+    {0xA64A0000u, 44u}, // ksj -> Latn
+    {0xC64A0000u, 44u}, // ksr -> Latn
+    {0x866A0000u, 19u}, // ktb -> Ethi
+    {0xB26A0000u, 44u}, // ktm -> Latn
+    {0xBA6A0000u, 44u}, // kto -> Latn
+    {0x6B750000u, 44u}, // ku -> Latn
     {0x6B754952u,  1u}, // ku-IR -> Arab
     {0x6B754C42u,  1u}, // ku-LB -> Arab
-    {0x868A0000u, 40u}, // kub -> Latn
-    {0x8E8A0000u, 40u}, // kud -> Latn
-    {0x928A0000u, 40u}, // kue -> Latn
-    {0xA68A0000u, 40u}, // kuj -> Latn
-    {0xB28A0000u, 15u}, // kum -> Cyrl
-    {0xB68A0000u, 40u}, // kun -> Latn
-    {0xBE8A0000u, 40u}, // kup -> Latn
-    {0xCA8A0000u, 40u}, // kus -> Latn
-    {0x6B760000u, 15u}, // kv -> Cyrl
-    {0x9AAA0000u, 40u}, // kvg -> Latn
-    {0xC6AA0000u, 40u}, // kvr -> Latn
+    {0x868A0000u, 44u}, // kub -> Latn
+    {0x8E8A0000u, 44u}, // kud -> Latn
+    {0x928A0000u, 44u}, // kue -> Latn
+    {0xA68A0000u, 44u}, // kuj -> Latn
+    {0xB28A0000u, 16u}, // kum -> Cyrl
+    {0xB68A0000u, 44u}, // kun -> Latn
+    {0xBE8A0000u, 44u}, // kup -> Latn
+    {0xCA8A0000u, 44u}, // kus -> Latn
+    {0x6B760000u, 16u}, // kv -> Cyrl
+    {0x9AAA0000u, 44u}, // kvg -> Latn
+    {0xC6AA0000u, 44u}, // kvr -> Latn
     {0xDEAA0000u,  1u}, // kvx -> Arab
-    {0x6B770000u, 40u}, // kw -> Latn
-    {0xA6CA0000u, 40u}, // kwj -> Latn
-    {0xBACA0000u, 40u}, // kwo -> Latn
-    {0x82EA0000u, 40u}, // kxa -> Latn
-    {0x8AEA0000u, 18u}, // kxc -> Ethi
-    {0xB2EA0000u, 80u}, // kxm -> Thai
+    {0x6B770000u, 44u}, // kw -> Latn
+    {0xA6CA0000u, 44u}, // kwj -> Latn
+    {0xBACA0000u, 44u}, // kwo -> Latn
+    {0x82EA0000u, 44u}, // kxa -> Latn
+    {0x8AEA0000u, 19u}, // kxc -> Ethi
+    {0xB2EA0000u, 87u}, // kxm -> Thai
     {0xBEEA0000u,  1u}, // kxp -> Arab
-    {0xDAEA0000u, 40u}, // kxw -> Latn
-    {0xE6EA0000u, 40u}, // kxz -> Latn
-    {0x6B790000u, 15u}, // ky -> Cyrl
+    {0xDAEA0000u, 44u}, // kxw -> Latn
+    {0xE6EA0000u, 44u}, // kxz -> Latn
+    {0x6B790000u, 16u}, // ky -> Cyrl
     {0x6B79434Eu,  1u}, // ky-CN -> Arab
-    {0x6B795452u, 40u}, // ky-TR -> Latn
-    {0x930A0000u, 40u}, // kye -> Latn
-    {0xDF0A0000u, 40u}, // kyx -> Latn
-    {0xC72A0000u, 40u}, // kzr -> Latn
-    {0x6C610000u, 40u}, // la -> Latn
-    {0x840B0000u, 42u}, // lab -> Lina
-    {0x8C0B0000u, 27u}, // lad -> Hebr
-    {0x980B0000u, 40u}, // lag -> Latn
+    {0x6B795452u, 44u}, // ky-TR -> Latn
+    {0x930A0000u, 44u}, // kye -> Latn
+    {0xDF0A0000u, 44u}, // kyx -> Latn
+    {0xC72A0000u, 44u}, // kzr -> Latn
+    {0x6C610000u, 44u}, // la -> Latn
+    {0x840B0000u, 46u}, // lab -> Lina
+    {0x8C0B0000u, 30u}, // lad -> Hebr
+    {0x980B0000u, 44u}, // lag -> Latn
     {0x9C0B0000u,  1u}, // lah -> Arab
-    {0xA40B0000u, 40u}, // laj -> Latn
-    {0xC80B0000u, 40u}, // las -> Latn
-    {0x6C620000u, 40u}, // lb -> Latn
-    {0x902B0000u, 15u}, // lbe -> Cyrl
-    {0xD02B0000u, 40u}, // lbu -> Latn
-    {0xD82B0000u, 40u}, // lbw -> Latn
-    {0xB04B0000u, 40u}, // lcm -> Latn
-    {0xBC4B0000u, 80u}, // lcp -> Thai
-    {0x846B0000u, 40u}, // ldb -> Latn
-    {0x8C8B0000u, 40u}, // led -> Latn
-    {0x908B0000u, 40u}, // lee -> Latn
-    {0xB08B0000u, 40u}, // lem -> Latn
-    {0xBC8B0000u, 41u}, // lep -> Lepc
-    {0xC08B0000u, 40u}, // leq -> Latn
-    {0xD08B0000u, 40u}, // leu -> Latn
-    {0xE48B0000u, 15u}, // lez -> Cyrl
-    {0x6C670000u, 40u}, // lg -> Latn
-    {0x98CB0000u, 40u}, // lgg -> Latn
-    {0x6C690000u, 40u}, // li -> Latn
-    {0x810B0000u, 40u}, // lia -> Latn
-    {0x8D0B0000u, 40u}, // lid -> Latn
-    {0x950B0000u, 16u}, // lif -> Deva
-    {0x990B0000u, 40u}, // lig -> Latn
-    {0x9D0B0000u, 40u}, // lih -> Latn
-    {0xA50B0000u, 40u}, // lij -> Latn
-    {0xC90B0000u, 43u}, // lis -> Lisu
-    {0xBD2B0000u, 40u}, // ljp -> Latn
+    {0xA40B0000u, 44u}, // laj -> Latn
+    {0xC80B0000u, 44u}, // las -> Latn
+    {0x6C620000u, 44u}, // lb -> Latn
+    {0x902B0000u, 16u}, // lbe -> Cyrl
+    {0xD02B0000u, 44u}, // lbu -> Latn
+    {0xD82B0000u, 44u}, // lbw -> Latn
+    {0xB04B0000u, 44u}, // lcm -> Latn
+    {0xBC4B0000u, 87u}, // lcp -> Thai
+    {0x846B0000u, 44u}, // ldb -> Latn
+    {0x8C8B0000u, 44u}, // led -> Latn
+    {0x908B0000u, 44u}, // lee -> Latn
+    {0xB08B0000u, 44u}, // lem -> Latn
+    {0xBC8B0000u, 45u}, // lep -> Lepc
+    {0xC08B0000u, 44u}, // leq -> Latn
+    {0xD08B0000u, 44u}, // leu -> Latn
+    {0xE48B0000u, 16u}, // lez -> Cyrl
+    {0x6C670000u, 44u}, // lg -> Latn
+    {0x98CB0000u, 44u}, // lgg -> Latn
+    {0x6C690000u, 44u}, // li -> Latn
+    {0x810B0000u, 44u}, // lia -> Latn
+    {0x8D0B0000u, 44u}, // lid -> Latn
+    {0x950B0000u, 17u}, // lif -> Deva
+    {0x990B0000u, 44u}, // lig -> Latn
+    {0x9D0B0000u, 44u}, // lih -> Latn
+    {0xA50B0000u, 44u}, // lij -> Latn
+    {0xC90B0000u, 47u}, // lis -> Lisu
+    {0xBD2B0000u, 44u}, // ljp -> Latn
     {0xA14B0000u,  1u}, // lki -> Arab
-    {0xCD4B0000u, 40u}, // lkt -> Latn
-    {0x916B0000u, 40u}, // lle -> Latn
-    {0xB56B0000u, 40u}, // lln -> Latn
-    {0xB58B0000u, 77u}, // lmn -> Telu
-    {0xB98B0000u, 40u}, // lmo -> Latn
-    {0xBD8B0000u, 40u}, // lmp -> Latn
-    {0x6C6E0000u, 40u}, // ln -> Latn
-    {0xC9AB0000u, 40u}, // lns -> Latn
-    {0xD1AB0000u, 40u}, // lnu -> Latn
-    {0x6C6F0000u, 39u}, // lo -> Laoo
-    {0xA5CB0000u, 40u}, // loj -> Latn
-    {0xA9CB0000u, 40u}, // lok -> Latn
-    {0xADCB0000u, 40u}, // lol -> Latn
-    {0xC5CB0000u, 40u}, // lor -> Latn
-    {0xC9CB0000u, 40u}, // los -> Latn
-    {0xE5CB0000u, 40u}, // loz -> Latn
+    {0xCD4B0000u, 44u}, // lkt -> Latn
+    {0x916B0000u, 44u}, // lle -> Latn
+    {0xB56B0000u, 44u}, // lln -> Latn
+    {0xB58B0000u, 84u}, // lmn -> Telu
+    {0xB98B0000u, 44u}, // lmo -> Latn
+    {0xBD8B0000u, 44u}, // lmp -> Latn
+    {0x6C6E0000u, 44u}, // ln -> Latn
+    {0xC9AB0000u, 44u}, // lns -> Latn
+    {0xD1AB0000u, 44u}, // lnu -> Latn
+    {0x6C6F0000u, 43u}, // lo -> Laoo
+    {0xA5CB0000u, 44u}, // loj -> Latn
+    {0xA9CB0000u, 44u}, // lok -> Latn
+    {0xADCB0000u, 44u}, // lol -> Latn
+    {0xC5CB0000u, 44u}, // lor -> Latn
+    {0xC9CB0000u, 44u}, // los -> Latn
+    {0xE5CB0000u, 44u}, // loz -> Latn
     {0x8A2B0000u,  1u}, // lrc -> Arab
-    {0x6C740000u, 40u}, // lt -> Latn
-    {0x9A6B0000u, 40u}, // ltg -> Latn
-    {0x6C750000u, 40u}, // lu -> Latn
-    {0x828B0000u, 40u}, // lua -> Latn
-    {0xBA8B0000u, 40u}, // luo -> Latn
-    {0xE28B0000u, 40u}, // luy -> Latn
+    {0x6C740000u, 44u}, // lt -> Latn
+    {0x9A6B0000u, 44u}, // ltg -> Latn
+    {0x6C750000u, 44u}, // lu -> Latn
+    {0x828B0000u, 44u}, // lua -> Latn
+    {0xBA8B0000u, 44u}, // luo -> Latn
+    {0xE28B0000u, 44u}, // luy -> Latn
     {0xE68B0000u,  1u}, // luz -> Arab
-    {0x6C760000u, 40u}, // lv -> Latn
-    {0xAECB0000u, 80u}, // lwl -> Thai
-    {0x9F2B0000u, 24u}, // lzh -> Hans
-    {0xE72B0000u, 40u}, // lzz -> Latn
-    {0x8C0C0000u, 40u}, // mad -> Latn
-    {0x940C0000u, 40u}, // maf -> Latn
-    {0x980C0000u, 16u}, // mag -> Deva
-    {0xA00C0000u, 16u}, // mai -> Deva
-    {0xA80C0000u, 40u}, // mak -> Latn
-    {0xB40C0000u, 40u}, // man -> Latn
-    {0xB40C474Eu, 54u}, // man-GN -> Nkoo
-    {0xC80C0000u, 40u}, // mas -> Latn
-    {0xD80C0000u, 40u}, // maw -> Latn
-    {0xE40C0000u, 40u}, // maz -> Latn
-    {0x9C2C0000u, 40u}, // mbh -> Latn
-    {0xB82C0000u, 40u}, // mbo -> Latn
-    {0xC02C0000u, 40u}, // mbq -> Latn
-    {0xD02C0000u, 40u}, // mbu -> Latn
-    {0xD82C0000u, 40u}, // mbw -> Latn
-    {0xA04C0000u, 40u}, // mci -> Latn
-    {0xBC4C0000u, 40u}, // mcp -> Latn
-    {0xC04C0000u, 40u}, // mcq -> Latn
-    {0xC44C0000u, 40u}, // mcr -> Latn
-    {0xD04C0000u, 40u}, // mcu -> Latn
-    {0x806C0000u, 40u}, // mda -> Latn
+    {0x6C760000u, 44u}, // lv -> Latn
+    {0xAECB0000u, 87u}, // lwl -> Thai
+    {0x9F2B0000u, 27u}, // lzh -> Hans
+    {0xE72B0000u, 44u}, // lzz -> Latn
+    {0x8C0C0000u, 44u}, // mad -> Latn
+    {0x940C0000u, 44u}, // maf -> Latn
+    {0x980C0000u, 17u}, // mag -> Deva
+    {0xA00C0000u, 17u}, // mai -> Deva
+    {0xA80C0000u, 44u}, // mak -> Latn
+    {0xB40C0000u, 44u}, // man -> Latn
+    {0xB40C474Eu, 58u}, // man-GN -> Nkoo
+    {0xC80C0000u, 44u}, // mas -> Latn
+    {0xD80C0000u, 44u}, // maw -> Latn
+    {0xE40C0000u, 44u}, // maz -> Latn
+    {0x9C2C0000u, 44u}, // mbh -> Latn
+    {0xB82C0000u, 44u}, // mbo -> Latn
+    {0xC02C0000u, 44u}, // mbq -> Latn
+    {0xD02C0000u, 44u}, // mbu -> Latn
+    {0xD82C0000u, 44u}, // mbw -> Latn
+    {0xA04C0000u, 44u}, // mci -> Latn
+    {0xBC4C0000u, 44u}, // mcp -> Latn
+    {0xC04C0000u, 44u}, // mcq -> Latn
+    {0xC44C0000u, 44u}, // mcr -> Latn
+    {0xD04C0000u, 44u}, // mcu -> Latn
+    {0x806C0000u, 44u}, // mda -> Latn
     {0x906C0000u,  1u}, // mde -> Arab
-    {0x946C0000u, 15u}, // mdf -> Cyrl
-    {0x9C6C0000u, 40u}, // mdh -> Latn
-    {0xA46C0000u, 40u}, // mdj -> Latn
-    {0xC46C0000u, 40u}, // mdr -> Latn
-    {0xDC6C0000u, 18u}, // mdx -> Ethi
-    {0x8C8C0000u, 40u}, // med -> Latn
-    {0x908C0000u, 40u}, // mee -> Latn
-    {0xA88C0000u, 40u}, // mek -> Latn
-    {0xB48C0000u, 40u}, // men -> Latn
-    {0xC48C0000u, 40u}, // mer -> Latn
-    {0xCC8C0000u, 40u}, // met -> Latn
-    {0xD08C0000u, 40u}, // meu -> Latn
+    {0x946C0000u, 16u}, // mdf -> Cyrl
+    {0x9C6C0000u, 44u}, // mdh -> Latn
+    {0xA46C0000u, 44u}, // mdj -> Latn
+    {0xC46C0000u, 44u}, // mdr -> Latn
+    {0xDC6C0000u, 19u}, // mdx -> Ethi
+    {0x8C8C0000u, 44u}, // med -> Latn
+    {0x908C0000u, 44u}, // mee -> Latn
+    {0xA88C0000u, 44u}, // mek -> Latn
+    {0xB48C0000u, 44u}, // men -> Latn
+    {0xC48C0000u, 44u}, // mer -> Latn
+    {0xCC8C0000u, 44u}, // met -> Latn
+    {0xD08C0000u, 44u}, // meu -> Latn
     {0x80AC0000u,  1u}, // mfa -> Arab
-    {0x90AC0000u, 40u}, // mfe -> Latn
-    {0xB4AC0000u, 40u}, // mfn -> Latn
-    {0xB8AC0000u, 40u}, // mfo -> Latn
-    {0xC0AC0000u, 40u}, // mfq -> Latn
-    {0x6D670000u, 40u}, // mg -> Latn
-    {0x9CCC0000u, 40u}, // mgh -> Latn
-    {0xACCC0000u, 40u}, // mgl -> Latn
-    {0xB8CC0000u, 40u}, // mgo -> Latn
-    {0xBCCC0000u, 16u}, // mgp -> Deva
-    {0xE0CC0000u, 40u}, // mgy -> Latn
-    {0x6D680000u, 40u}, // mh -> Latn
-    {0xA0EC0000u, 40u}, // mhi -> Latn
-    {0xACEC0000u, 40u}, // mhl -> Latn
-    {0x6D690000u, 40u}, // mi -> Latn
-    {0x950C0000u, 40u}, // mif -> Latn
-    {0xB50C0000u, 40u}, // min -> Latn
-    {0xC90C0000u, 26u}, // mis -> Hatr
-    {0xD90C0000u, 40u}, // miw -> Latn
-    {0x6D6B0000u, 15u}, // mk -> Cyrl
+    {0x90AC0000u, 44u}, // mfe -> Latn
+    {0xB4AC0000u, 44u}, // mfn -> Latn
+    {0xB8AC0000u, 44u}, // mfo -> Latn
+    {0xC0AC0000u, 44u}, // mfq -> Latn
+    {0x6D670000u, 44u}, // mg -> Latn
+    {0x9CCC0000u, 44u}, // mgh -> Latn
+    {0xACCC0000u, 44u}, // mgl -> Latn
+    {0xB8CC0000u, 44u}, // mgo -> Latn
+    {0xBCCC0000u, 17u}, // mgp -> Deva
+    {0xE0CC0000u, 44u}, // mgy -> Latn
+    {0x6D680000u, 44u}, // mh -> Latn
+    {0xA0EC0000u, 44u}, // mhi -> Latn
+    {0xACEC0000u, 44u}, // mhl -> Latn
+    {0x6D690000u, 44u}, // mi -> Latn
+    {0x950C0000u, 44u}, // mif -> Latn
+    {0xB50C0000u, 44u}, // min -> Latn
+    {0xC90C0000u, 29u}, // mis -> Hatr
+    {0xD90C0000u, 44u}, // miw -> Latn
+    {0x6D6B0000u, 16u}, // mk -> Cyrl
     {0xA14C0000u,  1u}, // mki -> Arab
-    {0xAD4C0000u, 40u}, // mkl -> Latn
-    {0xBD4C0000u, 40u}, // mkp -> Latn
-    {0xD94C0000u, 40u}, // mkw -> Latn
-    {0x6D6C0000u, 49u}, // ml -> Mlym
-    {0x916C0000u, 40u}, // mle -> Latn
-    {0xBD6C0000u, 40u}, // mlp -> Latn
-    {0xC96C0000u, 40u}, // mls -> Latn
-    {0xB98C0000u, 40u}, // mmo -> Latn
-    {0xD18C0000u, 40u}, // mmu -> Latn
-    {0xDD8C0000u, 40u}, // mmx -> Latn
-    {0x6D6E0000u, 15u}, // mn -> Cyrl
-    {0x6D6E434Eu, 50u}, // mn-CN -> Mong
-    {0x81AC0000u, 40u}, // mna -> Latn
-    {0x95AC0000u, 40u}, // mnf -> Latn
+    {0xAD4C0000u, 44u}, // mkl -> Latn
+    {0xBD4C0000u, 44u}, // mkp -> Latn
+    {0xD94C0000u, 44u}, // mkw -> Latn
+    {0x6D6C0000u, 53u}, // ml -> Mlym
+    {0x916C0000u, 44u}, // mle -> Latn
+    {0xBD6C0000u, 44u}, // mlp -> Latn
+    {0xC96C0000u, 44u}, // mls -> Latn
+    {0xB98C0000u, 44u}, // mmo -> Latn
+    {0xD18C0000u, 44u}, // mmu -> Latn
+    {0xDD8C0000u, 44u}, // mmx -> Latn
+    {0x6D6E0000u, 16u}, // mn -> Cyrl
+    {0x6D6E434Eu, 54u}, // mn-CN -> Mong
+    {0x81AC0000u, 44u}, // mna -> Latn
+    {0x95AC0000u, 44u}, // mnf -> Latn
     {0xA1AC0000u,  7u}, // mni -> Beng
-    {0xD9AC0000u, 52u}, // mnw -> Mymr
-    {0x81CC0000u, 40u}, // moa -> Latn
-    {0x91CC0000u, 40u}, // moe -> Latn
-    {0x9DCC0000u, 40u}, // moh -> Latn
-    {0xC9CC0000u, 40u}, // mos -> Latn
-    {0xDDCC0000u, 40u}, // mox -> Latn
-    {0xBDEC0000u, 40u}, // mpp -> Latn
-    {0xC9EC0000u, 40u}, // mps -> Latn
-    {0xCDEC0000u, 40u}, // mpt -> Latn
-    {0xDDEC0000u, 40u}, // mpx -> Latn
-    {0xAE0C0000u, 40u}, // mql -> Latn
-    {0x6D720000u, 16u}, // mr -> Deva
-    {0x8E2C0000u, 16u}, // mrd -> Deva
-    {0xA62C0000u, 15u}, // mrj -> Cyrl
-    {0xBA2C0000u, 51u}, // mro -> Mroo
-    {0x6D730000u, 40u}, // ms -> Latn
+    {0xD9AC0000u, 56u}, // mnw -> Mymr
+    {0x81CC0000u, 44u}, // moa -> Latn
+    {0x91CC0000u, 44u}, // moe -> Latn
+    {0x9DCC0000u, 44u}, // moh -> Latn
+    {0xC9CC0000u, 44u}, // mos -> Latn
+    {0xDDCC0000u, 44u}, // mox -> Latn
+    {0xBDEC0000u, 44u}, // mpp -> Latn
+    {0xC9EC0000u, 44u}, // mps -> Latn
+    {0xCDEC0000u, 44u}, // mpt -> Latn
+    {0xDDEC0000u, 44u}, // mpx -> Latn
+    {0xAE0C0000u, 44u}, // mql -> Latn
+    {0x6D720000u, 17u}, // mr -> Deva
+    {0x8E2C0000u, 17u}, // mrd -> Deva
+    {0xA62C0000u, 16u}, // mrj -> Cyrl
+    {0xBA2C0000u, 55u}, // mro -> Mroo
+    {0x6D730000u, 44u}, // ms -> Latn
     {0x6D734343u,  1u}, // ms-CC -> Arab
     {0x6D734944u,  1u}, // ms-ID -> Arab
-    {0x6D740000u, 40u}, // mt -> Latn
-    {0x8A6C0000u, 40u}, // mtc -> Latn
-    {0x966C0000u, 40u}, // mtf -> Latn
-    {0xA26C0000u, 40u}, // mti -> Latn
-    {0xC66C0000u, 16u}, // mtr -> Deva
-    {0x828C0000u, 40u}, // mua -> Latn
-    {0xC68C0000u, 40u}, // mur -> Latn
-    {0xCA8C0000u, 40u}, // mus -> Latn
-    {0x82AC0000u, 40u}, // mva -> Latn
-    {0xB6AC0000u, 40u}, // mvn -> Latn
+    {0x6D740000u, 44u}, // mt -> Latn
+    {0x8A6C0000u, 44u}, // mtc -> Latn
+    {0x966C0000u, 44u}, // mtf -> Latn
+    {0xA26C0000u, 44u}, // mti -> Latn
+    {0xC66C0000u, 17u}, // mtr -> Deva
+    {0x828C0000u, 44u}, // mua -> Latn
+    {0xC68C0000u, 44u}, // mur -> Latn
+    {0xCA8C0000u, 44u}, // mus -> Latn
+    {0x82AC0000u, 44u}, // mva -> Latn
+    {0xB6AC0000u, 44u}, // mvn -> Latn
     {0xE2AC0000u,  1u}, // mvy -> Arab
-    {0xAACC0000u, 40u}, // mwk -> Latn
-    {0xC6CC0000u, 16u}, // mwr -> Deva
-    {0xD6CC0000u, 40u}, // mwv -> Latn
-    {0x8AEC0000u, 40u}, // mxc -> Latn
-    {0xB2EC0000u, 40u}, // mxm -> Latn
-    {0x6D790000u, 52u}, // my -> Mymr
-    {0xAB0C0000u, 40u}, // myk -> Latn
-    {0xB30C0000u, 18u}, // mym -> Ethi
-    {0xD70C0000u, 15u}, // myv -> Cyrl
-    {0xDB0C0000u, 40u}, // myw -> Latn
-    {0xDF0C0000u, 40u}, // myx -> Latn
-    {0xE70C0000u, 46u}, // myz -> Mand
-    {0xAB2C0000u, 40u}, // mzk -> Latn
-    {0xB32C0000u, 40u}, // mzm -> Latn
+    {0xAACC0000u, 44u}, // mwk -> Latn
+    {0xC6CC0000u, 17u}, // mwr -> Deva
+    {0xD6CC0000u, 44u}, // mwv -> Latn
+    {0xDACC0000u, 33u}, // mww -> Hmnp
+    {0x8AEC0000u, 44u}, // mxc -> Latn
+    {0xB2EC0000u, 44u}, // mxm -> Latn
+    {0x6D790000u, 56u}, // my -> Mymr
+    {0xAB0C0000u, 44u}, // myk -> Latn
+    {0xB30C0000u, 19u}, // mym -> Ethi
+    {0xD70C0000u, 16u}, // myv -> Cyrl
+    {0xDB0C0000u, 44u}, // myw -> Latn
+    {0xDF0C0000u, 44u}, // myx -> Latn
+    {0xE70C0000u, 50u}, // myz -> Mand
+    {0xAB2C0000u, 44u}, // mzk -> Latn
+    {0xB32C0000u, 44u}, // mzm -> Latn
     {0xB72C0000u,  1u}, // mzn -> Arab
-    {0xBF2C0000u, 40u}, // mzp -> Latn
-    {0xDB2C0000u, 40u}, // mzw -> Latn
-    {0xE72C0000u, 40u}, // mzz -> Latn
-    {0x6E610000u, 40u}, // na -> Latn
-    {0x880D0000u, 40u}, // nac -> Latn
-    {0x940D0000u, 40u}, // naf -> Latn
-    {0xA80D0000u, 40u}, // nak -> Latn
-    {0xB40D0000u, 24u}, // nan -> Hans
-    {0xBC0D0000u, 40u}, // nap -> Latn
-    {0xC00D0000u, 40u}, // naq -> Latn
-    {0xC80D0000u, 40u}, // nas -> Latn
-    {0x6E620000u, 40u}, // nb -> Latn
-    {0x804D0000u, 40u}, // nca -> Latn
-    {0x904D0000u, 40u}, // nce -> Latn
-    {0x944D0000u, 40u}, // ncf -> Latn
-    {0x9C4D0000u, 40u}, // nch -> Latn
-    {0xB84D0000u, 40u}, // nco -> Latn
-    {0xD04D0000u, 40u}, // ncu -> Latn
-    {0x6E640000u, 40u}, // nd -> Latn
-    {0x886D0000u, 40u}, // ndc -> Latn
-    {0xC86D0000u, 40u}, // nds -> Latn
-    {0x6E650000u, 16u}, // ne -> Deva
-    {0x848D0000u, 40u}, // neb -> Latn
-    {0xD88D0000u, 16u}, // new -> Deva
-    {0xDC8D0000u, 40u}, // nex -> Latn
-    {0xC4AD0000u, 40u}, // nfr -> Latn
-    {0x6E670000u, 40u}, // ng -> Latn
-    {0x80CD0000u, 40u}, // nga -> Latn
-    {0x84CD0000u, 40u}, // ngb -> Latn
-    {0xACCD0000u, 40u}, // ngl -> Latn
-    {0x84ED0000u, 40u}, // nhb -> Latn
-    {0x90ED0000u, 40u}, // nhe -> Latn
-    {0xD8ED0000u, 40u}, // nhw -> Latn
-    {0x950D0000u, 40u}, // nif -> Latn
-    {0xA10D0000u, 40u}, // nii -> Latn
-    {0xA50D0000u, 40u}, // nij -> Latn
-    {0xB50D0000u, 40u}, // nin -> Latn
-    {0xD10D0000u, 40u}, // niu -> Latn
-    {0xE10D0000u, 40u}, // niy -> Latn
-    {0xE50D0000u, 40u}, // niz -> Latn
-    {0xB92D0000u, 40u}, // njo -> Latn
-    {0x994D0000u, 40u}, // nkg -> Latn
-    {0xB94D0000u, 40u}, // nko -> Latn
-    {0x6E6C0000u, 40u}, // nl -> Latn
-    {0x998D0000u, 40u}, // nmg -> Latn
-    {0xE58D0000u, 40u}, // nmz -> Latn
-    {0x6E6E0000u, 40u}, // nn -> Latn
-    {0x95AD0000u, 40u}, // nnf -> Latn
-    {0x9DAD0000u, 40u}, // nnh -> Latn
-    {0xA9AD0000u, 40u}, // nnk -> Latn
-    {0xB1AD0000u, 40u}, // nnm -> Latn
-    {0x6E6F0000u, 40u}, // no -> Latn
-    {0x8DCD0000u, 38u}, // nod -> Lana
-    {0x91CD0000u, 16u}, // noe -> Deva
-    {0xB5CD0000u, 64u}, // non -> Runr
-    {0xBDCD0000u, 40u}, // nop -> Latn
-    {0xD1CD0000u, 40u}, // nou -> Latn
-    {0xBA0D0000u, 54u}, // nqo -> Nkoo
-    {0x6E720000u, 40u}, // nr -> Latn
-    {0x862D0000u, 40u}, // nrb -> Latn
-    {0xAA4D0000u,  9u}, // nsk -> Cans
-    {0xB64D0000u, 40u}, // nsn -> Latn
-    {0xBA4D0000u, 40u}, // nso -> Latn
-    {0xCA4D0000u, 40u}, // nss -> Latn
-    {0xB26D0000u, 40u}, // ntm -> Latn
-    {0xC66D0000u, 40u}, // ntr -> Latn
-    {0xA28D0000u, 40u}, // nui -> Latn
-    {0xBE8D0000u, 40u}, // nup -> Latn
-    {0xCA8D0000u, 40u}, // nus -> Latn
-    {0xD68D0000u, 40u}, // nuv -> Latn
-    {0xDE8D0000u, 40u}, // nux -> Latn
-    {0x6E760000u, 40u}, // nv -> Latn
-    {0x86CD0000u, 40u}, // nwb -> Latn
-    {0xC2ED0000u, 40u}, // nxq -> Latn
-    {0xC6ED0000u, 40u}, // nxr -> Latn
-    {0x6E790000u, 40u}, // ny -> Latn
-    {0xB30D0000u, 40u}, // nym -> Latn
-    {0xB70D0000u, 40u}, // nyn -> Latn
-    {0xA32D0000u, 40u}, // nzi -> Latn
-    {0x6F630000u, 40u}, // oc -> Latn
-    {0x88CE0000u, 40u}, // ogc -> Latn
-    {0xC54E0000u, 40u}, // okr -> Latn
-    {0xD54E0000u, 40u}, // okv -> Latn
-    {0x6F6D0000u, 40u}, // om -> Latn
-    {0x99AE0000u, 40u}, // ong -> Latn
-    {0xB5AE0000u, 40u}, // onn -> Latn
-    {0xC9AE0000u, 40u}, // ons -> Latn
-    {0xB1EE0000u, 40u}, // opm -> Latn
-    {0x6F720000u, 57u}, // or -> Orya
-    {0xBA2E0000u, 40u}, // oro -> Latn
+    {0xBF2C0000u, 44u}, // mzp -> Latn
+    {0xDB2C0000u, 44u}, // mzw -> Latn
+    {0xE72C0000u, 44u}, // mzz -> Latn
+    {0x6E610000u, 44u}, // na -> Latn
+    {0x880D0000u, 44u}, // nac -> Latn
+    {0x940D0000u, 44u}, // naf -> Latn
+    {0xA80D0000u, 44u}, // nak -> Latn
+    {0xB40D0000u, 27u}, // nan -> Hans
+    {0xBC0D0000u, 44u}, // nap -> Latn
+    {0xC00D0000u, 44u}, // naq -> Latn
+    {0xC80D0000u, 44u}, // nas -> Latn
+    {0x6E620000u, 44u}, // nb -> Latn
+    {0x804D0000u, 44u}, // nca -> Latn
+    {0x904D0000u, 44u}, // nce -> Latn
+    {0x944D0000u, 44u}, // ncf -> Latn
+    {0x9C4D0000u, 44u}, // nch -> Latn
+    {0xB84D0000u, 44u}, // nco -> Latn
+    {0xD04D0000u, 44u}, // ncu -> Latn
+    {0x6E640000u, 44u}, // nd -> Latn
+    {0x886D0000u, 44u}, // ndc -> Latn
+    {0xC86D0000u, 44u}, // nds -> Latn
+    {0x6E650000u, 17u}, // ne -> Deva
+    {0x848D0000u, 44u}, // neb -> Latn
+    {0xD88D0000u, 17u}, // new -> Deva
+    {0xDC8D0000u, 44u}, // nex -> Latn
+    {0xC4AD0000u, 44u}, // nfr -> Latn
+    {0x6E670000u, 44u}, // ng -> Latn
+    {0x80CD0000u, 44u}, // nga -> Latn
+    {0x84CD0000u, 44u}, // ngb -> Latn
+    {0xACCD0000u, 44u}, // ngl -> Latn
+    {0x84ED0000u, 44u}, // nhb -> Latn
+    {0x90ED0000u, 44u}, // nhe -> Latn
+    {0xD8ED0000u, 44u}, // nhw -> Latn
+    {0x950D0000u, 44u}, // nif -> Latn
+    {0xA10D0000u, 44u}, // nii -> Latn
+    {0xA50D0000u, 44u}, // nij -> Latn
+    {0xB50D0000u, 44u}, // nin -> Latn
+    {0xD10D0000u, 44u}, // niu -> Latn
+    {0xE10D0000u, 44u}, // niy -> Latn
+    {0xE50D0000u, 44u}, // niz -> Latn
+    {0xB92D0000u, 44u}, // njo -> Latn
+    {0x994D0000u, 44u}, // nkg -> Latn
+    {0xB94D0000u, 44u}, // nko -> Latn
+    {0x6E6C0000u, 44u}, // nl -> Latn
+    {0x998D0000u, 44u}, // nmg -> Latn
+    {0xE58D0000u, 44u}, // nmz -> Latn
+    {0x6E6E0000u, 44u}, // nn -> Latn
+    {0x95AD0000u, 44u}, // nnf -> Latn
+    {0x9DAD0000u, 44u}, // nnh -> Latn
+    {0xA9AD0000u, 44u}, // nnk -> Latn
+    {0xB1AD0000u, 44u}, // nnm -> Latn
+    {0xBDAD0000u, 91u}, // nnp -> Wcho
+    {0x6E6F0000u, 44u}, // no -> Latn
+    {0x8DCD0000u, 42u}, // nod -> Lana
+    {0x91CD0000u, 17u}, // noe -> Deva
+    {0xB5CD0000u, 69u}, // non -> Runr
+    {0xBDCD0000u, 44u}, // nop -> Latn
+    {0xD1CD0000u, 44u}, // nou -> Latn
+    {0xBA0D0000u, 58u}, // nqo -> Nkoo
+    {0x6E720000u, 44u}, // nr -> Latn
+    {0x862D0000u, 44u}, // nrb -> Latn
+    {0xAA4D0000u, 10u}, // nsk -> Cans
+    {0xB64D0000u, 44u}, // nsn -> Latn
+    {0xBA4D0000u, 44u}, // nso -> Latn
+    {0xCA4D0000u, 44u}, // nss -> Latn
+    {0xB26D0000u, 44u}, // ntm -> Latn
+    {0xC66D0000u, 44u}, // ntr -> Latn
+    {0xA28D0000u, 44u}, // nui -> Latn
+    {0xBE8D0000u, 44u}, // nup -> Latn
+    {0xCA8D0000u, 44u}, // nus -> Latn
+    {0xD68D0000u, 44u}, // nuv -> Latn
+    {0xDE8D0000u, 44u}, // nux -> Latn
+    {0x6E760000u, 44u}, // nv -> Latn
+    {0x86CD0000u, 44u}, // nwb -> Latn
+    {0xC2ED0000u, 44u}, // nxq -> Latn
+    {0xC6ED0000u, 44u}, // nxr -> Latn
+    {0x6E790000u, 44u}, // ny -> Latn
+    {0xB30D0000u, 44u}, // nym -> Latn
+    {0xB70D0000u, 44u}, // nyn -> Latn
+    {0xA32D0000u, 44u}, // nzi -> Latn
+    {0x6F630000u, 44u}, // oc -> Latn
+    {0x88CE0000u, 44u}, // ogc -> Latn
+    {0xC54E0000u, 44u}, // okr -> Latn
+    {0xD54E0000u, 44u}, // okv -> Latn
+    {0x6F6D0000u, 44u}, // om -> Latn
+    {0x99AE0000u, 44u}, // ong -> Latn
+    {0xB5AE0000u, 44u}, // onn -> Latn
+    {0xC9AE0000u, 44u}, // ons -> Latn
+    {0xB1EE0000u, 44u}, // opm -> Latn
+    {0x6F720000u, 62u}, // or -> Orya
+    {0xBA2E0000u, 44u}, // oro -> Latn
     {0xD22E0000u,  1u}, // oru -> Arab
-    {0x6F730000u, 15u}, // os -> Cyrl
-    {0x824E0000u, 58u}, // osa -> Osge
+    {0x6F730000u, 16u}, // os -> Cyrl
+    {0x824E0000u, 63u}, // osa -> Osge
     {0x826E0000u,  1u}, // ota -> Arab
-    {0xAA6E0000u, 56u}, // otk -> Orkh
-    {0xB32E0000u, 40u}, // ozm -> Latn
-    {0x70610000u, 23u}, // pa -> Guru
+    {0xAA6E0000u, 61u}, // otk -> Orkh
+    {0xB32E0000u, 44u}, // ozm -> Latn
+    {0x70610000u, 26u}, // pa -> Guru
     {0x7061504Bu,  1u}, // pa-PK -> Arab
-    {0x980F0000u, 40u}, // pag -> Latn
-    {0xAC0F0000u, 60u}, // pal -> Phli
-    {0xB00F0000u, 40u}, // pam -> Latn
-    {0xBC0F0000u, 40u}, // pap -> Latn
-    {0xD00F0000u, 40u}, // pau -> Latn
-    {0xA02F0000u, 40u}, // pbi -> Latn
-    {0x8C4F0000u, 40u}, // pcd -> Latn
-    {0xB04F0000u, 40u}, // pcm -> Latn
-    {0x886F0000u, 40u}, // pdc -> Latn
-    {0xCC6F0000u, 40u}, // pdt -> Latn
-    {0x8C8F0000u, 40u}, // ped -> Latn
-    {0xB88F0000u, 84u}, // peo -> Xpeo
-    {0xDC8F0000u, 40u}, // pex -> Latn
-    {0xACAF0000u, 40u}, // pfl -> Latn
+    {0x980F0000u, 44u}, // pag -> Latn
+    {0xAC0F0000u, 65u}, // pal -> Phli
+    {0xB00F0000u, 44u}, // pam -> Latn
+    {0xBC0F0000u, 44u}, // pap -> Latn
+    {0xD00F0000u, 44u}, // pau -> Latn
+    {0xA02F0000u, 44u}, // pbi -> Latn
+    {0x8C4F0000u, 44u}, // pcd -> Latn
+    {0xB04F0000u, 44u}, // pcm -> Latn
+    {0x886F0000u, 44u}, // pdc -> Latn
+    {0xCC6F0000u, 44u}, // pdt -> Latn
+    {0x8C8F0000u, 44u}, // ped -> Latn
+    {0xB88F0000u, 92u}, // peo -> Xpeo
+    {0xDC8F0000u, 44u}, // pex -> Latn
+    {0xACAF0000u, 44u}, // pfl -> Latn
     {0xACEF0000u,  1u}, // phl -> Arab
-    {0xB4EF0000u, 61u}, // phn -> Phnx
-    {0xAD0F0000u, 40u}, // pil -> Latn
-    {0xBD0F0000u, 40u}, // pip -> Latn
+    {0xB4EF0000u, 66u}, // phn -> Phnx
+    {0xAD0F0000u, 44u}, // pil -> Latn
+    {0xBD0F0000u, 44u}, // pip -> Latn
     {0x814F0000u,  8u}, // pka -> Brah
-    {0xB94F0000u, 40u}, // pko -> Latn
-    {0x706C0000u, 40u}, // pl -> Latn
-    {0x816F0000u, 40u}, // pla -> Latn
-    {0xC98F0000u, 40u}, // pms -> Latn
-    {0x99AF0000u, 40u}, // png -> Latn
-    {0xB5AF0000u, 40u}, // pnn -> Latn
-    {0xCDAF0000u, 21u}, // pnt -> Grek
-    {0xB5CF0000u, 40u}, // pon -> Latn
-    {0xB9EF0000u, 40u}, // ppo -> Latn
-    {0x822F0000u, 34u}, // pra -> Khar
+    {0xB94F0000u, 44u}, // pko -> Latn
+    {0x706C0000u, 44u}, // pl -> Latn
+    {0x816F0000u, 44u}, // pla -> Latn
+    {0xC98F0000u, 44u}, // pms -> Latn
+    {0x99AF0000u, 44u}, // png -> Latn
+    {0xB5AF0000u, 44u}, // pnn -> Latn
+    {0xCDAF0000u, 24u}, // pnt -> Grek
+    {0xB5CF0000u, 44u}, // pon -> Latn
+    {0xB9EF0000u, 44u}, // ppo -> Latn
+    {0x822F0000u, 38u}, // pra -> Khar
     {0x8E2F0000u,  1u}, // prd -> Arab
-    {0x9A2F0000u, 40u}, // prg -> Latn
+    {0x9A2F0000u, 44u}, // prg -> Latn
     {0x70730000u,  1u}, // ps -> Arab
-    {0xCA4F0000u, 40u}, // pss -> Latn
-    {0x70740000u, 40u}, // pt -> Latn
-    {0xBE6F0000u, 40u}, // ptp -> Latn
-    {0xD28F0000u, 40u}, // puu -> Latn
-    {0x82CF0000u, 40u}, // pwa -> Latn
-    {0x71750000u, 40u}, // qu -> Latn
-    {0x8A900000u, 40u}, // quc -> Latn
-    {0x9A900000u, 40u}, // qug -> Latn
-    {0xA0110000u, 40u}, // rai -> Latn
-    {0xA4110000u, 16u}, // raj -> Deva
-    {0xB8110000u, 40u}, // rao -> Latn
-    {0x94510000u, 40u}, // rcf -> Latn
-    {0xA4910000u, 40u}, // rej -> Latn
-    {0xAC910000u, 40u}, // rel -> Latn
-    {0xC8910000u, 40u}, // res -> Latn
-    {0xB4D10000u, 40u}, // rgn -> Latn
+    {0xCA4F0000u, 44u}, // pss -> Latn
+    {0x70740000u, 44u}, // pt -> Latn
+    {0xBE6F0000u, 44u}, // ptp -> Latn
+    {0xD28F0000u, 44u}, // puu -> Latn
+    {0x82CF0000u, 44u}, // pwa -> Latn
+    {0x71750000u, 44u}, // qu -> Latn
+    {0x8A900000u, 44u}, // quc -> Latn
+    {0x9A900000u, 44u}, // qug -> Latn
+    {0xA0110000u, 44u}, // rai -> Latn
+    {0xA4110000u, 17u}, // raj -> Deva
+    {0xB8110000u, 44u}, // rao -> Latn
+    {0x94510000u, 44u}, // rcf -> Latn
+    {0xA4910000u, 44u}, // rej -> Latn
+    {0xAC910000u, 44u}, // rel -> Latn
+    {0xC8910000u, 44u}, // res -> Latn
+    {0xB4D10000u, 44u}, // rgn -> Latn
     {0x98F10000u,  1u}, // rhg -> Arab
-    {0x81110000u, 40u}, // ria -> Latn
-    {0x95110000u, 78u}, // rif -> Tfng
-    {0x95114E4Cu, 40u}, // rif-NL -> Latn
-    {0xC9310000u, 16u}, // rjs -> Deva
+    {0x81110000u, 44u}, // ria -> Latn
+    {0x95110000u, 85u}, // rif -> Tfng
+    {0x95114E4Cu, 44u}, // rif-NL -> Latn
+    {0xC9310000u, 17u}, // rjs -> Deva
     {0xCD510000u,  7u}, // rkt -> Beng
-    {0x726D0000u, 40u}, // rm -> Latn
-    {0x95910000u, 40u}, // rmf -> Latn
-    {0xB9910000u, 40u}, // rmo -> Latn
+    {0x726D0000u, 44u}, // rm -> Latn
+    {0x95910000u, 44u}, // rmf -> Latn
+    {0xB9910000u, 44u}, // rmo -> Latn
     {0xCD910000u,  1u}, // rmt -> Arab
-    {0xD1910000u, 40u}, // rmu -> Latn
-    {0x726E0000u, 40u}, // rn -> Latn
-    {0x81B10000u, 40u}, // rna -> Latn
-    {0x99B10000u, 40u}, // rng -> Latn
-    {0x726F0000u, 40u}, // ro -> Latn
-    {0x85D10000u, 40u}, // rob -> Latn
-    {0x95D10000u, 40u}, // rof -> Latn
-    {0xB9D10000u, 40u}, // roo -> Latn
-    {0xBA310000u, 40u}, // rro -> Latn
-    {0xB2710000u, 40u}, // rtm -> Latn
-    {0x72750000u, 15u}, // ru -> Cyrl
-    {0x92910000u, 15u}, // rue -> Cyrl
-    {0x9A910000u, 40u}, // rug -> Latn
-    {0x72770000u, 40u}, // rw -> Latn
-    {0xAAD10000u, 40u}, // rwk -> Latn
-    {0xBAD10000u, 40u}, // rwo -> Latn
-    {0xD3110000u, 33u}, // ryu -> Kana
-    {0x73610000u, 16u}, // sa -> Deva
-    {0x94120000u, 40u}, // saf -> Latn
-    {0x9C120000u, 15u}, // sah -> Cyrl
-    {0xC0120000u, 40u}, // saq -> Latn
-    {0xC8120000u, 40u}, // sas -> Latn
-    {0xCC120000u, 40u}, // sat -> Latn
-    {0xE4120000u, 67u}, // saz -> Saur
-    {0x80320000u, 40u}, // sba -> Latn
-    {0x90320000u, 40u}, // sbe -> Latn
-    {0xBC320000u, 40u}, // sbp -> Latn
-    {0x73630000u, 40u}, // sc -> Latn
-    {0xA8520000u, 16u}, // sck -> Deva
+    {0xD1910000u, 44u}, // rmu -> Latn
+    {0x726E0000u, 44u}, // rn -> Latn
+    {0x81B10000u, 44u}, // rna -> Latn
+    {0x99B10000u, 44u}, // rng -> Latn
+    {0x726F0000u, 44u}, // ro -> Latn
+    {0x85D10000u, 44u}, // rob -> Latn
+    {0x95D10000u, 44u}, // rof -> Latn
+    {0xB9D10000u, 44u}, // roo -> Latn
+    {0xBA310000u, 44u}, // rro -> Latn
+    {0xB2710000u, 44u}, // rtm -> Latn
+    {0x72750000u, 16u}, // ru -> Cyrl
+    {0x92910000u, 16u}, // rue -> Cyrl
+    {0x9A910000u, 44u}, // rug -> Latn
+    {0x72770000u, 44u}, // rw -> Latn
+    {0xAAD10000u, 44u}, // rwk -> Latn
+    {0xBAD10000u, 44u}, // rwo -> Latn
+    {0xD3110000u, 37u}, // ryu -> Kana
+    {0x73610000u, 17u}, // sa -> Deva
+    {0x94120000u, 44u}, // saf -> Latn
+    {0x9C120000u, 16u}, // sah -> Cyrl
+    {0xC0120000u, 44u}, // saq -> Latn
+    {0xC8120000u, 44u}, // sas -> Latn
+    {0xCC120000u, 44u}, // sat -> Latn
+    {0xD4120000u, 44u}, // sav -> Latn
+    {0xE4120000u, 72u}, // saz -> Saur
+    {0x80320000u, 44u}, // sba -> Latn
+    {0x90320000u, 44u}, // sbe -> Latn
+    {0xBC320000u, 44u}, // sbp -> Latn
+    {0x73630000u, 44u}, // sc -> Latn
+    {0xA8520000u, 17u}, // sck -> Deva
     {0xAC520000u,  1u}, // scl -> Arab
-    {0xB4520000u, 40u}, // scn -> Latn
-    {0xB8520000u, 40u}, // sco -> Latn
-    {0xC8520000u, 40u}, // scs -> Latn
+    {0xB4520000u, 44u}, // scn -> Latn
+    {0xB8520000u, 44u}, // sco -> Latn
+    {0xC8520000u, 44u}, // scs -> Latn
     {0x73640000u,  1u}, // sd -> Arab
-    {0x88720000u, 40u}, // sdc -> Latn
+    {0x88720000u, 44u}, // sdc -> Latn
     {0x9C720000u,  1u}, // sdh -> Arab
-    {0x73650000u, 40u}, // se -> Latn
-    {0x94920000u, 40u}, // sef -> Latn
-    {0x9C920000u, 40u}, // seh -> Latn
-    {0xA0920000u, 40u}, // sei -> Latn
-    {0xC8920000u, 40u}, // ses -> Latn
-    {0x73670000u, 40u}, // sg -> Latn
-    {0x80D20000u, 55u}, // sga -> Ogam
-    {0xC8D20000u, 40u}, // sgs -> Latn
-    {0xD8D20000u, 18u}, // sgw -> Ethi
-    {0xE4D20000u, 40u}, // sgz -> Latn
-    {0x73680000u, 40u}, // sh -> Latn
-    {0xA0F20000u, 78u}, // shi -> Tfng
-    {0xA8F20000u, 40u}, // shk -> Latn
-    {0xB4F20000u, 52u}, // shn -> Mymr
+    {0x73650000u, 44u}, // se -> Latn
+    {0x94920000u, 44u}, // sef -> Latn
+    {0x9C920000u, 44u}, // seh -> Latn
+    {0xA0920000u, 44u}, // sei -> Latn
+    {0xC8920000u, 44u}, // ses -> Latn
+    {0x73670000u, 44u}, // sg -> Latn
+    {0x80D20000u, 60u}, // sga -> Ogam
+    {0xC8D20000u, 44u}, // sgs -> Latn
+    {0xD8D20000u, 19u}, // sgw -> Ethi
+    {0xE4D20000u, 44u}, // sgz -> Latn
+    {0x73680000u, 44u}, // sh -> Latn
+    {0xA0F20000u, 85u}, // shi -> Tfng
+    {0xA8F20000u, 44u}, // shk -> Latn
+    {0xB4F20000u, 56u}, // shn -> Mymr
     {0xD0F20000u,  1u}, // shu -> Arab
-    {0x73690000u, 69u}, // si -> Sinh
-    {0x8D120000u, 40u}, // sid -> Latn
-    {0x99120000u, 40u}, // sig -> Latn
-    {0xAD120000u, 40u}, // sil -> Latn
-    {0xB1120000u, 40u}, // sim -> Latn
-    {0xC5320000u, 40u}, // sjr -> Latn
-    {0x736B0000u, 40u}, // sk -> Latn
-    {0x89520000u, 40u}, // skc -> Latn
+    {0x73690000u, 74u}, // si -> Sinh
+    {0x8D120000u, 44u}, // sid -> Latn
+    {0x99120000u, 44u}, // sig -> Latn
+    {0xAD120000u, 44u}, // sil -> Latn
+    {0xB1120000u, 44u}, // sim -> Latn
+    {0xC5320000u, 44u}, // sjr -> Latn
+    {0x736B0000u, 44u}, // sk -> Latn
+    {0x89520000u, 44u}, // skc -> Latn
     {0xC5520000u,  1u}, // skr -> Arab
-    {0xC9520000u, 40u}, // sks -> Latn
-    {0x736C0000u, 40u}, // sl -> Latn
-    {0x8D720000u, 40u}, // sld -> Latn
-    {0xA1720000u, 40u}, // sli -> Latn
-    {0xAD720000u, 40u}, // sll -> Latn
-    {0xE1720000u, 40u}, // sly -> Latn
-    {0x736D0000u, 40u}, // sm -> Latn
-    {0x81920000u, 40u}, // sma -> Latn
-    {0xA5920000u, 40u}, // smj -> Latn
-    {0xB5920000u, 40u}, // smn -> Latn
-    {0xBD920000u, 65u}, // smp -> Samr
-    {0xC1920000u, 40u}, // smq -> Latn
-    {0xC9920000u, 40u}, // sms -> Latn
-    {0x736E0000u, 40u}, // sn -> Latn
-    {0x89B20000u, 40u}, // snc -> Latn
-    {0xA9B20000u, 40u}, // snk -> Latn
-    {0xBDB20000u, 40u}, // snp -> Latn
-    {0xDDB20000u, 40u}, // snx -> Latn
-    {0xE1B20000u, 40u}, // sny -> Latn
-    {0x736F0000u, 40u}, // so -> Latn
-    {0xA9D20000u, 40u}, // sok -> Latn
-    {0xC1D20000u, 40u}, // soq -> Latn
-    {0xD1D20000u, 80u}, // sou -> Thai
-    {0xE1D20000u, 40u}, // soy -> Latn
-    {0x8DF20000u, 40u}, // spd -> Latn
-    {0xADF20000u, 40u}, // spl -> Latn
-    {0xC9F20000u, 40u}, // sps -> Latn
-    {0x73710000u, 40u}, // sq -> Latn
-    {0x73720000u, 15u}, // sr -> Cyrl
-    {0x73724D45u, 40u}, // sr-ME -> Latn
-    {0x7372524Fu, 40u}, // sr-RO -> Latn
-    {0x73725255u, 40u}, // sr-RU -> Latn
-    {0x73725452u, 40u}, // sr-TR -> Latn
-    {0x86320000u, 70u}, // srb -> Sora
-    {0xB6320000u, 40u}, // srn -> Latn
-    {0xC6320000u, 40u}, // srr -> Latn
-    {0xDE320000u, 16u}, // srx -> Deva
-    {0x73730000u, 40u}, // ss -> Latn
-    {0x8E520000u, 40u}, // ssd -> Latn
-    {0x9A520000u, 40u}, // ssg -> Latn
-    {0xE2520000u, 40u}, // ssy -> Latn
-    {0x73740000u, 40u}, // st -> Latn
-    {0xAA720000u, 40u}, // stk -> Latn
-    {0xC2720000u, 40u}, // stq -> Latn
-    {0x73750000u, 40u}, // su -> Latn
-    {0x82920000u, 40u}, // sua -> Latn
-    {0x92920000u, 40u}, // sue -> Latn
-    {0xAA920000u, 40u}, // suk -> Latn
-    {0xC6920000u, 40u}, // sur -> Latn
-    {0xCA920000u, 40u}, // sus -> Latn
-    {0x73760000u, 40u}, // sv -> Latn
-    {0x73770000u, 40u}, // sw -> Latn
+    {0xC9520000u, 44u}, // sks -> Latn
+    {0x736C0000u, 44u}, // sl -> Latn
+    {0x8D720000u, 44u}, // sld -> Latn
+    {0xA1720000u, 44u}, // sli -> Latn
+    {0xAD720000u, 44u}, // sll -> Latn
+    {0xE1720000u, 44u}, // sly -> Latn
+    {0x736D0000u, 44u}, // sm -> Latn
+    {0x81920000u, 44u}, // sma -> Latn
+    {0xA5920000u, 44u}, // smj -> Latn
+    {0xB5920000u, 44u}, // smn -> Latn
+    {0xBD920000u, 70u}, // smp -> Samr
+    {0xC1920000u, 44u}, // smq -> Latn
+    {0xC9920000u, 44u}, // sms -> Latn
+    {0x736E0000u, 44u}, // sn -> Latn
+    {0x89B20000u, 44u}, // snc -> Latn
+    {0xA9B20000u, 44u}, // snk -> Latn
+    {0xBDB20000u, 44u}, // snp -> Latn
+    {0xDDB20000u, 44u}, // snx -> Latn
+    {0xE1B20000u, 44u}, // sny -> Latn
+    {0x736F0000u, 44u}, // so -> Latn
+    {0x99D20000u, 75u}, // sog -> Sogd
+    {0xA9D20000u, 44u}, // sok -> Latn
+    {0xC1D20000u, 44u}, // soq -> Latn
+    {0xD1D20000u, 87u}, // sou -> Thai
+    {0xE1D20000u, 44u}, // soy -> Latn
+    {0x8DF20000u, 44u}, // spd -> Latn
+    {0xADF20000u, 44u}, // spl -> Latn
+    {0xC9F20000u, 44u}, // sps -> Latn
+    {0x73710000u, 44u}, // sq -> Latn
+    {0x73720000u, 16u}, // sr -> Cyrl
+    {0x73724D45u, 44u}, // sr-ME -> Latn
+    {0x7372524Fu, 44u}, // sr-RO -> Latn
+    {0x73725255u, 44u}, // sr-RU -> Latn
+    {0x73725452u, 44u}, // sr-TR -> Latn
+    {0x86320000u, 76u}, // srb -> Sora
+    {0xB6320000u, 44u}, // srn -> Latn
+    {0xC6320000u, 44u}, // srr -> Latn
+    {0xDE320000u, 17u}, // srx -> Deva
+    {0x73730000u, 44u}, // ss -> Latn
+    {0x8E520000u, 44u}, // ssd -> Latn
+    {0x9A520000u, 44u}, // ssg -> Latn
+    {0xE2520000u, 44u}, // ssy -> Latn
+    {0x73740000u, 44u}, // st -> Latn
+    {0xAA720000u, 44u}, // stk -> Latn
+    {0xC2720000u, 44u}, // stq -> Latn
+    {0x73750000u, 44u}, // su -> Latn
+    {0x82920000u, 44u}, // sua -> Latn
+    {0x92920000u, 44u}, // sue -> Latn
+    {0xAA920000u, 44u}, // suk -> Latn
+    {0xC6920000u, 44u}, // sur -> Latn
+    {0xCA920000u, 44u}, // sus -> Latn
+    {0x73760000u, 44u}, // sv -> Latn
+    {0x73770000u, 44u}, // sw -> Latn
     {0x86D20000u,  1u}, // swb -> Arab
-    {0x8AD20000u, 40u}, // swc -> Latn
-    {0x9AD20000u, 40u}, // swg -> Latn
-    {0xBED20000u, 40u}, // swp -> Latn
-    {0xD6D20000u, 16u}, // swv -> Deva
-    {0xB6F20000u, 40u}, // sxn -> Latn
-    {0xDAF20000u, 40u}, // sxw -> Latn
+    {0x8AD20000u, 44u}, // swc -> Latn
+    {0x9AD20000u, 44u}, // swg -> Latn
+    {0xBED20000u, 44u}, // swp -> Latn
+    {0xD6D20000u, 17u}, // swv -> Deva
+    {0xB6F20000u, 44u}, // sxn -> Latn
+    {0xDAF20000u, 44u}, // sxw -> Latn
     {0xAF120000u,  7u}, // syl -> Beng
-    {0xC7120000u, 71u}, // syr -> Syrc
-    {0xAF320000u, 40u}, // szl -> Latn
-    {0x74610000u, 74u}, // ta -> Taml
-    {0xA4130000u, 16u}, // taj -> Deva
-    {0xAC130000u, 40u}, // tal -> Latn
-    {0xB4130000u, 40u}, // tan -> Latn
-    {0xC0130000u, 40u}, // taq -> Latn
-    {0x88330000u, 40u}, // tbc -> Latn
-    {0x8C330000u, 40u}, // tbd -> Latn
-    {0x94330000u, 40u}, // tbf -> Latn
-    {0x98330000u, 40u}, // tbg -> Latn
-    {0xB8330000u, 40u}, // tbo -> Latn
-    {0xD8330000u, 40u}, // tbw -> Latn
-    {0xE4330000u, 40u}, // tbz -> Latn
-    {0xA0530000u, 40u}, // tci -> Latn
-    {0xE0530000u, 36u}, // tcy -> Knda
-    {0x8C730000u, 72u}, // tdd -> Tale
-    {0x98730000u, 16u}, // tdg -> Deva
-    {0x9C730000u, 16u}, // tdh -> Deva
-    {0x74650000u, 77u}, // te -> Telu
-    {0x8C930000u, 40u}, // ted -> Latn
-    {0xB0930000u, 40u}, // tem -> Latn
-    {0xB8930000u, 40u}, // teo -> Latn
-    {0xCC930000u, 40u}, // tet -> Latn
-    {0xA0B30000u, 40u}, // tfi -> Latn
-    {0x74670000u, 15u}, // tg -> Cyrl
+    {0xC7120000u, 78u}, // syr -> Syrc
+    {0xAF320000u, 44u}, // szl -> Latn
+    {0x74610000u, 81u}, // ta -> Taml
+    {0xA4130000u, 17u}, // taj -> Deva
+    {0xAC130000u, 44u}, // tal -> Latn
+    {0xB4130000u, 44u}, // tan -> Latn
+    {0xC0130000u, 44u}, // taq -> Latn
+    {0x88330000u, 44u}, // tbc -> Latn
+    {0x8C330000u, 44u}, // tbd -> Latn
+    {0x94330000u, 44u}, // tbf -> Latn
+    {0x98330000u, 44u}, // tbg -> Latn
+    {0xB8330000u, 44u}, // tbo -> Latn
+    {0xD8330000u, 44u}, // tbw -> Latn
+    {0xE4330000u, 44u}, // tbz -> Latn
+    {0xA0530000u, 44u}, // tci -> Latn
+    {0xE0530000u, 40u}, // tcy -> Knda
+    {0x8C730000u, 79u}, // tdd -> Tale
+    {0x98730000u, 17u}, // tdg -> Deva
+    {0x9C730000u, 17u}, // tdh -> Deva
+    {0x74650000u, 84u}, // te -> Telu
+    {0x8C930000u, 44u}, // ted -> Latn
+    {0xB0930000u, 44u}, // tem -> Latn
+    {0xB8930000u, 44u}, // teo -> Latn
+    {0xCC930000u, 44u}, // tet -> Latn
+    {0xA0B30000u, 44u}, // tfi -> Latn
+    {0x74670000u, 16u}, // tg -> Cyrl
     {0x7467504Bu,  1u}, // tg-PK -> Arab
-    {0x88D30000u, 40u}, // tgc -> Latn
-    {0xB8D30000u, 40u}, // tgo -> Latn
-    {0xD0D30000u, 40u}, // tgu -> Latn
-    {0x74680000u, 80u}, // th -> Thai
-    {0xACF30000u, 16u}, // thl -> Deva
-    {0xC0F30000u, 16u}, // thq -> Deva
-    {0xC4F30000u, 16u}, // thr -> Deva
-    {0x74690000u, 18u}, // ti -> Ethi
-    {0x95130000u, 40u}, // tif -> Latn
-    {0x99130000u, 18u}, // tig -> Ethi
-    {0xA9130000u, 40u}, // tik -> Latn
-    {0xB1130000u, 40u}, // tim -> Latn
-    {0xB9130000u, 40u}, // tio -> Latn
-    {0xD5130000u, 40u}, // tiv -> Latn
-    {0x746B0000u, 40u}, // tk -> Latn
-    {0xAD530000u, 40u}, // tkl -> Latn
-    {0xC5530000u, 40u}, // tkr -> Latn
-    {0xCD530000u, 16u}, // tkt -> Deva
-    {0x746C0000u, 40u}, // tl -> Latn
-    {0x95730000u, 40u}, // tlf -> Latn
-    {0xDD730000u, 40u}, // tlx -> Latn
-    {0xE1730000u, 40u}, // tly -> Latn
-    {0x9D930000u, 40u}, // tmh -> Latn
-    {0xE1930000u, 40u}, // tmy -> Latn
-    {0x746E0000u, 40u}, // tn -> Latn
-    {0x9DB30000u, 40u}, // tnh -> Latn
-    {0x746F0000u, 40u}, // to -> Latn
-    {0x95D30000u, 40u}, // tof -> Latn
-    {0x99D30000u, 40u}, // tog -> Latn
-    {0xC1D30000u, 40u}, // toq -> Latn
-    {0xA1F30000u, 40u}, // tpi -> Latn
-    {0xB1F30000u, 40u}, // tpm -> Latn
-    {0xE5F30000u, 40u}, // tpz -> Latn
-    {0xBA130000u, 40u}, // tqo -> Latn
-    {0x74720000u, 40u}, // tr -> Latn
-    {0xD2330000u, 40u}, // tru -> Latn
-    {0xD6330000u, 40u}, // trv -> Latn
+    {0x88D30000u, 44u}, // tgc -> Latn
+    {0xB8D30000u, 44u}, // tgo -> Latn
+    {0xD0D30000u, 44u}, // tgu -> Latn
+    {0x74680000u, 87u}, // th -> Thai
+    {0xACF30000u, 17u}, // thl -> Deva
+    {0xC0F30000u, 17u}, // thq -> Deva
+    {0xC4F30000u, 17u}, // thr -> Deva
+    {0x74690000u, 19u}, // ti -> Ethi
+    {0x95130000u, 44u}, // tif -> Latn
+    {0x99130000u, 19u}, // tig -> Ethi
+    {0xA9130000u, 44u}, // tik -> Latn
+    {0xB1130000u, 44u}, // tim -> Latn
+    {0xB9130000u, 44u}, // tio -> Latn
+    {0xD5130000u, 44u}, // tiv -> Latn
+    {0x746B0000u, 44u}, // tk -> Latn
+    {0xAD530000u, 44u}, // tkl -> Latn
+    {0xC5530000u, 44u}, // tkr -> Latn
+    {0xCD530000u, 17u}, // tkt -> Deva
+    {0x746C0000u, 44u}, // tl -> Latn
+    {0x95730000u, 44u}, // tlf -> Latn
+    {0xDD730000u, 44u}, // tlx -> Latn
+    {0xE1730000u, 44u}, // tly -> Latn
+    {0x9D930000u, 44u}, // tmh -> Latn
+    {0xE1930000u, 44u}, // tmy -> Latn
+    {0x746E0000u, 44u}, // tn -> Latn
+    {0x9DB30000u, 44u}, // tnh -> Latn
+    {0x746F0000u, 44u}, // to -> Latn
+    {0x95D30000u, 44u}, // tof -> Latn
+    {0x99D30000u, 44u}, // tog -> Latn
+    {0xC1D30000u, 44u}, // toq -> Latn
+    {0xA1F30000u, 44u}, // tpi -> Latn
+    {0xB1F30000u, 44u}, // tpm -> Latn
+    {0xE5F30000u, 44u}, // tpz -> Latn
+    {0xBA130000u, 44u}, // tqo -> Latn
+    {0x74720000u, 44u}, // tr -> Latn
+    {0xD2330000u, 44u}, // tru -> Latn
+    {0xD6330000u, 44u}, // trv -> Latn
     {0xDA330000u,  1u}, // trw -> Arab
-    {0x74730000u, 40u}, // ts -> Latn
-    {0x8E530000u, 21u}, // tsd -> Grek
-    {0x96530000u, 16u}, // tsf -> Deva
-    {0x9A530000u, 40u}, // tsg -> Latn
-    {0xA6530000u, 81u}, // tsj -> Tibt
-    {0xDA530000u, 40u}, // tsw -> Latn
-    {0x74740000u, 15u}, // tt -> Cyrl
-    {0x8E730000u, 40u}, // ttd -> Latn
-    {0x92730000u, 40u}, // tte -> Latn
-    {0xA6730000u, 40u}, // ttj -> Latn
-    {0xC6730000u, 40u}, // ttr -> Latn
-    {0xCA730000u, 80u}, // tts -> Thai
-    {0xCE730000u, 40u}, // ttt -> Latn
-    {0x9E930000u, 40u}, // tuh -> Latn
-    {0xAE930000u, 40u}, // tul -> Latn
-    {0xB2930000u, 40u}, // tum -> Latn
-    {0xC2930000u, 40u}, // tuq -> Latn
-    {0x8EB30000u, 40u}, // tvd -> Latn
-    {0xAEB30000u, 40u}, // tvl -> Latn
-    {0xD2B30000u, 40u}, // tvu -> Latn
-    {0x9ED30000u, 40u}, // twh -> Latn
-    {0xC2D30000u, 40u}, // twq -> Latn
-    {0x9AF30000u, 75u}, // txg -> Tang
-    {0x74790000u, 40u}, // ty -> Latn
-    {0x83130000u, 40u}, // tya -> Latn
-    {0xD7130000u, 15u}, // tyv -> Cyrl
-    {0xB3330000u, 40u}, // tzm -> Latn
-    {0xD0340000u, 40u}, // ubu -> Latn
-    {0xB0740000u, 15u}, // udm -> Cyrl
+    {0x74730000u, 44u}, // ts -> Latn
+    {0x8E530000u, 24u}, // tsd -> Grek
+    {0x96530000u, 17u}, // tsf -> Deva
+    {0x9A530000u, 44u}, // tsg -> Latn
+    {0xA6530000u, 88u}, // tsj -> Tibt
+    {0xDA530000u, 44u}, // tsw -> Latn
+    {0x74740000u, 16u}, // tt -> Cyrl
+    {0x8E730000u, 44u}, // ttd -> Latn
+    {0x92730000u, 44u}, // tte -> Latn
+    {0xA6730000u, 44u}, // ttj -> Latn
+    {0xC6730000u, 44u}, // ttr -> Latn
+    {0xCA730000u, 87u}, // tts -> Thai
+    {0xCE730000u, 44u}, // ttt -> Latn
+    {0x9E930000u, 44u}, // tuh -> Latn
+    {0xAE930000u, 44u}, // tul -> Latn
+    {0xB2930000u, 44u}, // tum -> Latn
+    {0xC2930000u, 44u}, // tuq -> Latn
+    {0x8EB30000u, 44u}, // tvd -> Latn
+    {0xAEB30000u, 44u}, // tvl -> Latn
+    {0xD2B30000u, 44u}, // tvu -> Latn
+    {0x9ED30000u, 44u}, // twh -> Latn
+    {0xC2D30000u, 44u}, // twq -> Latn
+    {0x9AF30000u, 82u}, // txg -> Tang
+    {0x74790000u, 44u}, // ty -> Latn
+    {0x83130000u, 44u}, // tya -> Latn
+    {0xD7130000u, 16u}, // tyv -> Cyrl
+    {0xB3330000u, 44u}, // tzm -> Latn
+    {0xD0340000u, 44u}, // ubu -> Latn
+    {0xB0740000u, 16u}, // udm -> Cyrl
     {0x75670000u,  1u}, // ug -> Arab
-    {0x75674B5Au, 15u}, // ug-KZ -> Cyrl
-    {0x75674D4Eu, 15u}, // ug-MN -> Cyrl
-    {0x80D40000u, 82u}, // uga -> Ugar
-    {0x756B0000u, 15u}, // uk -> Cyrl
-    {0xA1740000u, 40u}, // uli -> Latn
-    {0x85940000u, 40u}, // umb -> Latn
+    {0x75674B5Au, 16u}, // ug-KZ -> Cyrl
+    {0x75674D4Eu, 16u}, // ug-MN -> Cyrl
+    {0x80D40000u, 89u}, // uga -> Ugar
+    {0x756B0000u, 16u}, // uk -> Cyrl
+    {0xA1740000u, 44u}, // uli -> Latn
+    {0x85940000u, 44u}, // umb -> Latn
     {0xC5B40000u,  7u}, // unr -> Beng
-    {0xC5B44E50u, 16u}, // unr-NP -> Deva
+    {0xC5B44E50u, 17u}, // unr-NP -> Deva
     {0xDDB40000u,  7u}, // unx -> Beng
     {0x75720000u,  1u}, // ur -> Arab
-    {0xA2340000u, 40u}, // uri -> Latn
-    {0xCE340000u, 40u}, // urt -> Latn
-    {0xDA340000u, 40u}, // urw -> Latn
-    {0x82540000u, 40u}, // usa -> Latn
-    {0xC6740000u, 40u}, // utr -> Latn
-    {0x9EB40000u, 40u}, // uvh -> Latn
-    {0xAEB40000u, 40u}, // uvl -> Latn
-    {0x757A0000u, 40u}, // uz -> Latn
+    {0xA2340000u, 44u}, // uri -> Latn
+    {0xCE340000u, 44u}, // urt -> Latn
+    {0xDA340000u, 44u}, // urw -> Latn
+    {0x82540000u, 44u}, // usa -> Latn
+    {0xC6740000u, 44u}, // utr -> Latn
+    {0x9EB40000u, 44u}, // uvh -> Latn
+    {0xAEB40000u, 44u}, // uvl -> Latn
+    {0x757A0000u, 44u}, // uz -> Latn
     {0x757A4146u,  1u}, // uz-AF -> Arab
-    {0x757A434Eu, 15u}, // uz-CN -> Cyrl
-    {0x98150000u, 40u}, // vag -> Latn
-    {0xA0150000u, 83u}, // vai -> Vaii
-    {0xB4150000u, 40u}, // van -> Latn
-    {0x76650000u, 40u}, // ve -> Latn
-    {0x88950000u, 40u}, // vec -> Latn
-    {0xBC950000u, 40u}, // vep -> Latn
-    {0x76690000u, 40u}, // vi -> Latn
-    {0x89150000u, 40u}, // vic -> Latn
-    {0xD5150000u, 40u}, // viv -> Latn
-    {0xC9750000u, 40u}, // vls -> Latn
-    {0x95950000u, 40u}, // vmf -> Latn
-    {0xD9950000u, 40u}, // vmw -> Latn
-    {0x766F0000u, 40u}, // vo -> Latn
-    {0xCDD50000u, 40u}, // vot -> Latn
-    {0xBA350000u, 40u}, // vro -> Latn
-    {0xB6950000u, 40u}, // vun -> Latn
-    {0xCE950000u, 40u}, // vut -> Latn
-    {0x77610000u, 40u}, // wa -> Latn
-    {0x90160000u, 40u}, // wae -> Latn
-    {0xA4160000u, 40u}, // waj -> Latn
-    {0xAC160000u, 18u}, // wal -> Ethi
-    {0xB4160000u, 40u}, // wan -> Latn
-    {0xC4160000u, 40u}, // war -> Latn
-    {0xBC360000u, 40u}, // wbp -> Latn
-    {0xC0360000u, 77u}, // wbq -> Telu
-    {0xC4360000u, 16u}, // wbr -> Deva
-    {0xA0560000u, 40u}, // wci -> Latn
-    {0xC4960000u, 40u}, // wer -> Latn
-    {0xA0D60000u, 40u}, // wgi -> Latn
-    {0x98F60000u, 40u}, // whg -> Latn
-    {0x85160000u, 40u}, // wib -> Latn
-    {0xD1160000u, 40u}, // wiu -> Latn
-    {0xD5160000u, 40u}, // wiv -> Latn
-    {0x81360000u, 40u}, // wja -> Latn
-    {0xA1360000u, 40u}, // wji -> Latn
-    {0xC9760000u, 40u}, // wls -> Latn
-    {0xB9960000u, 40u}, // wmo -> Latn
-    {0x89B60000u, 40u}, // wnc -> Latn
+    {0x757A434Eu, 16u}, // uz-CN -> Cyrl
+    {0x98150000u, 44u}, // vag -> Latn
+    {0xA0150000u, 90u}, // vai -> Vaii
+    {0xB4150000u, 44u}, // van -> Latn
+    {0x76650000u, 44u}, // ve -> Latn
+    {0x88950000u, 44u}, // vec -> Latn
+    {0xBC950000u, 44u}, // vep -> Latn
+    {0x76690000u, 44u}, // vi -> Latn
+    {0x89150000u, 44u}, // vic -> Latn
+    {0xD5150000u, 44u}, // viv -> Latn
+    {0xC9750000u, 44u}, // vls -> Latn
+    {0x95950000u, 44u}, // vmf -> Latn
+    {0xD9950000u, 44u}, // vmw -> Latn
+    {0x766F0000u, 44u}, // vo -> Latn
+    {0xCDD50000u, 44u}, // vot -> Latn
+    {0xBA350000u, 44u}, // vro -> Latn
+    {0xB6950000u, 44u}, // vun -> Latn
+    {0xCE950000u, 44u}, // vut -> Latn
+    {0x77610000u, 44u}, // wa -> Latn
+    {0x90160000u, 44u}, // wae -> Latn
+    {0xA4160000u, 44u}, // waj -> Latn
+    {0xAC160000u, 19u}, // wal -> Ethi
+    {0xB4160000u, 44u}, // wan -> Latn
+    {0xC4160000u, 44u}, // war -> Latn
+    {0xBC360000u, 44u}, // wbp -> Latn
+    {0xC0360000u, 84u}, // wbq -> Telu
+    {0xC4360000u, 17u}, // wbr -> Deva
+    {0xA0560000u, 44u}, // wci -> Latn
+    {0xC4960000u, 44u}, // wer -> Latn
+    {0xA0D60000u, 44u}, // wgi -> Latn
+    {0x98F60000u, 44u}, // whg -> Latn
+    {0x85160000u, 44u}, // wib -> Latn
+    {0xD1160000u, 44u}, // wiu -> Latn
+    {0xD5160000u, 44u}, // wiv -> Latn
+    {0x81360000u, 44u}, // wja -> Latn
+    {0xA1360000u, 44u}, // wji -> Latn
+    {0xC9760000u, 44u}, // wls -> Latn
+    {0xB9960000u, 44u}, // wmo -> Latn
+    {0x89B60000u, 44u}, // wnc -> Latn
     {0xA1B60000u,  1u}, // wni -> Arab
-    {0xD1B60000u, 40u}, // wnu -> Latn
-    {0x776F0000u, 40u}, // wo -> Latn
-    {0x85D60000u, 40u}, // wob -> Latn
-    {0xC9D60000u, 40u}, // wos -> Latn
-    {0xCA360000u, 40u}, // wrs -> Latn
-    {0xAA560000u, 40u}, // wsk -> Latn
-    {0xB2760000u, 16u}, // wtm -> Deva
-    {0xD2960000u, 24u}, // wuu -> Hans
-    {0xD6960000u, 40u}, // wuv -> Latn
-    {0x82D60000u, 40u}, // wwa -> Latn
-    {0xD4170000u, 40u}, // xav -> Latn
-    {0xA0370000u, 40u}, // xbi -> Latn
-    {0xC4570000u, 10u}, // xcr -> Cari
-    {0xC8970000u, 40u}, // xes -> Latn
-    {0x78680000u, 40u}, // xh -> Latn
-    {0x81770000u, 40u}, // xla -> Latn
-    {0x89770000u, 44u}, // xlc -> Lyci
-    {0x8D770000u, 45u}, // xld -> Lydi
-    {0x95970000u, 19u}, // xmf -> Geor
-    {0xB5970000u, 47u}, // xmn -> Mani
-    {0xC5970000u, 48u}, // xmr -> Merc
-    {0x81B70000u, 53u}, // xna -> Narb
-    {0xC5B70000u, 16u}, // xnr -> Deva
-    {0x99D70000u, 40u}, // xog -> Latn
-    {0xB5D70000u, 40u}, // xon -> Latn
-    {0xC5F70000u, 63u}, // xpr -> Prti
-    {0x86370000u, 40u}, // xrb -> Latn
-    {0x82570000u, 66u}, // xsa -> Sarb
-    {0xA2570000u, 40u}, // xsi -> Latn
-    {0xB2570000u, 40u}, // xsm -> Latn
-    {0xC6570000u, 16u}, // xsr -> Deva
-    {0x92D70000u, 40u}, // xwe -> Latn
-    {0xB0180000u, 40u}, // yam -> Latn
-    {0xB8180000u, 40u}, // yao -> Latn
-    {0xBC180000u, 40u}, // yap -> Latn
-    {0xC8180000u, 40u}, // yas -> Latn
-    {0xCC180000u, 40u}, // yat -> Latn
-    {0xD4180000u, 40u}, // yav -> Latn
-    {0xE0180000u, 40u}, // yay -> Latn
-    {0xE4180000u, 40u}, // yaz -> Latn
-    {0x80380000u, 40u}, // yba -> Latn
-    {0x84380000u, 40u}, // ybb -> Latn
-    {0xE0380000u, 40u}, // yby -> Latn
-    {0xC4980000u, 40u}, // yer -> Latn
-    {0xC4D80000u, 40u}, // ygr -> Latn
-    {0xD8D80000u, 40u}, // ygw -> Latn
-    {0x79690000u, 27u}, // yi -> Hebr
-    {0xB9580000u, 40u}, // yko -> Latn
-    {0x91780000u, 40u}, // yle -> Latn
-    {0x99780000u, 40u}, // ylg -> Latn
-    {0xAD780000u, 40u}, // yll -> Latn
-    {0xAD980000u, 40u}, // yml -> Latn
-    {0x796F0000u, 40u}, // yo -> Latn
-    {0xB5D80000u, 40u}, // yon -> Latn
-    {0x86380000u, 40u}, // yrb -> Latn
-    {0x92380000u, 40u}, // yre -> Latn
-    {0xAE380000u, 40u}, // yrl -> Latn
-    {0xCA580000u, 40u}, // yss -> Latn
-    {0x82980000u, 40u}, // yua -> Latn
-    {0x92980000u, 25u}, // yue -> Hant
-    {0x9298434Eu, 24u}, // yue-CN -> Hans
-    {0xA6980000u, 40u}, // yuj -> Latn
-    {0xCE980000u, 40u}, // yut -> Latn
-    {0xDA980000u, 40u}, // yuw -> Latn
-    {0x7A610000u, 40u}, // za -> Latn
-    {0x98190000u, 40u}, // zag -> Latn
+    {0xD1B60000u, 44u}, // wnu -> Latn
+    {0x776F0000u, 44u}, // wo -> Latn
+    {0x85D60000u, 44u}, // wob -> Latn
+    {0xC9D60000u, 44u}, // wos -> Latn
+    {0xCA360000u, 44u}, // wrs -> Latn
+    {0x9A560000u, 21u}, // wsg -> Gong
+    {0xAA560000u, 44u}, // wsk -> Latn
+    {0xB2760000u, 17u}, // wtm -> Deva
+    {0xD2960000u, 27u}, // wuu -> Hans
+    {0xD6960000u, 44u}, // wuv -> Latn
+    {0x82D60000u, 44u}, // wwa -> Latn
+    {0xD4170000u, 44u}, // xav -> Latn
+    {0xA0370000u, 44u}, // xbi -> Latn
+    {0xC4570000u, 11u}, // xcr -> Cari
+    {0xC8970000u, 44u}, // xes -> Latn
+    {0x78680000u, 44u}, // xh -> Latn
+    {0x81770000u, 44u}, // xla -> Latn
+    {0x89770000u, 48u}, // xlc -> Lyci
+    {0x8D770000u, 49u}, // xld -> Lydi
+    {0x95970000u, 20u}, // xmf -> Geor
+    {0xB5970000u, 51u}, // xmn -> Mani
+    {0xC5970000u, 52u}, // xmr -> Merc
+    {0x81B70000u, 57u}, // xna -> Narb
+    {0xC5B70000u, 17u}, // xnr -> Deva
+    {0x99D70000u, 44u}, // xog -> Latn
+    {0xB5D70000u, 44u}, // xon -> Latn
+    {0xC5F70000u, 68u}, // xpr -> Prti
+    {0x86370000u, 44u}, // xrb -> Latn
+    {0x82570000u, 71u}, // xsa -> Sarb
+    {0xA2570000u, 44u}, // xsi -> Latn
+    {0xB2570000u, 44u}, // xsm -> Latn
+    {0xC6570000u, 17u}, // xsr -> Deva
+    {0x92D70000u, 44u}, // xwe -> Latn
+    {0xB0180000u, 44u}, // yam -> Latn
+    {0xB8180000u, 44u}, // yao -> Latn
+    {0xBC180000u, 44u}, // yap -> Latn
+    {0xC8180000u, 44u}, // yas -> Latn
+    {0xCC180000u, 44u}, // yat -> Latn
+    {0xD4180000u, 44u}, // yav -> Latn
+    {0xE0180000u, 44u}, // yay -> Latn
+    {0xE4180000u, 44u}, // yaz -> Latn
+    {0x80380000u, 44u}, // yba -> Latn
+    {0x84380000u, 44u}, // ybb -> Latn
+    {0xE0380000u, 44u}, // yby -> Latn
+    {0xC4980000u, 44u}, // yer -> Latn
+    {0xC4D80000u, 44u}, // ygr -> Latn
+    {0xD8D80000u, 44u}, // ygw -> Latn
+    {0x79690000u, 30u}, // yi -> Hebr
+    {0xB9580000u, 44u}, // yko -> Latn
+    {0x91780000u, 44u}, // yle -> Latn
+    {0x99780000u, 44u}, // ylg -> Latn
+    {0xAD780000u, 44u}, // yll -> Latn
+    {0xAD980000u, 44u}, // yml -> Latn
+    {0x796F0000u, 44u}, // yo -> Latn
+    {0xB5D80000u, 44u}, // yon -> Latn
+    {0x86380000u, 44u}, // yrb -> Latn
+    {0x92380000u, 44u}, // yre -> Latn
+    {0xAE380000u, 44u}, // yrl -> Latn
+    {0xCA580000u, 44u}, // yss -> Latn
+    {0x82980000u, 44u}, // yua -> Latn
+    {0x92980000u, 28u}, // yue -> Hant
+    {0x9298434Eu, 27u}, // yue-CN -> Hans
+    {0xA6980000u, 44u}, // yuj -> Latn
+    {0xCE980000u, 44u}, // yut -> Latn
+    {0xDA980000u, 44u}, // yuw -> Latn
+    {0x7A610000u, 44u}, // za -> Latn
+    {0x98190000u, 44u}, // zag -> Latn
     {0xA4790000u,  1u}, // zdj -> Arab
-    {0x80990000u, 40u}, // zea -> Latn
-    {0x9CD90000u, 78u}, // zgh -> Tfng
-    {0x7A680000u, 24u}, // zh -> Hans
-    {0x7A684155u, 25u}, // zh-AU -> Hant
-    {0x7A68424Eu, 25u}, // zh-BN -> Hant
-    {0x7A684742u, 25u}, // zh-GB -> Hant
-    {0x7A684746u, 25u}, // zh-GF -> Hant
-    {0x7A68484Bu, 25u}, // zh-HK -> Hant
-    {0x7A684944u, 25u}, // zh-ID -> Hant
-    {0x7A684D4Fu, 25u}, // zh-MO -> Hant
-    {0x7A684D59u, 25u}, // zh-MY -> Hant
-    {0x7A685041u, 25u}, // zh-PA -> Hant
-    {0x7A685046u, 25u}, // zh-PF -> Hant
-    {0x7A685048u, 25u}, // zh-PH -> Hant
-    {0x7A685352u, 25u}, // zh-SR -> Hant
-    {0x7A685448u, 25u}, // zh-TH -> Hant
-    {0x7A685457u, 25u}, // zh-TW -> Hant
-    {0x7A685553u, 25u}, // zh-US -> Hant
-    {0x7A68564Eu, 25u}, // zh-VN -> Hant
-    {0x81190000u, 40u}, // zia -> Latn
-    {0xB1790000u, 40u}, // zlm -> Latn
-    {0xA1990000u, 40u}, // zmi -> Latn
-    {0x91B90000u, 40u}, // zne -> Latn
-    {0x7A750000u, 40u}, // zu -> Latn
-    {0x83390000u, 40u}, // zza -> Latn
+    {0x80990000u, 44u}, // zea -> Latn
+    {0x9CD90000u, 85u}, // zgh -> Tfng
+    {0x7A680000u, 27u}, // zh -> Hans
+    {0x7A684155u, 28u}, // zh-AU -> Hant
+    {0x7A68424Eu, 28u}, // zh-BN -> Hant
+    {0x7A684742u, 28u}, // zh-GB -> Hant
+    {0x7A684746u, 28u}, // zh-GF -> Hant
+    {0x7A68484Bu, 28u}, // zh-HK -> Hant
+    {0x7A684944u, 28u}, // zh-ID -> Hant
+    {0x7A684D4Fu, 28u}, // zh-MO -> Hant
+    {0x7A684D59u, 28u}, // zh-MY -> Hant
+    {0x7A685041u, 28u}, // zh-PA -> Hant
+    {0x7A685046u, 28u}, // zh-PF -> Hant
+    {0x7A685048u, 28u}, // zh-PH -> Hant
+    {0x7A685352u, 28u}, // zh-SR -> Hant
+    {0x7A685448u, 28u}, // zh-TH -> Hant
+    {0x7A685457u, 28u}, // zh-TW -> Hant
+    {0x7A685553u, 28u}, // zh-US -> Hant
+    {0x7A68564Eu, 28u}, // zh-VN -> Hant
+    {0xDCF90000u, 59u}, // zhx -> Nshu
+    {0x81190000u, 44u}, // zia -> Latn
+    {0xB1790000u, 44u}, // zlm -> Latn
+    {0xA1990000u, 44u}, // zmi -> Latn
+    {0x91B90000u, 44u}, // zne -> Latn
+    {0x7A750000u, 44u}, // zu -> Latn
+    {0x83390000u, 44u}, // zza -> Latn
 });
 
 std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
@@ -1517,6 +1544,7 @@
     0xB5014E474C61746ELLU, // bin_Latn_NG
     0xA521494E44657661LLU, // bjj_Deva_IN
     0xB52149444C61746ELLU, // bjn_Latn_ID
+    0xCD21534E4C61746ELLU, // bjt_Latn_SN
     0xB141434D4C61746ELLU, // bkm_Latn_CM
     0xD14150484C61746ELLU, // bku_Latn_PH
     0xCD61564E54617674LLU, // blt_Tavt_VN
@@ -1546,7 +1574,6 @@
     0x93214D4C4C61746ELLU, // bze_Latn_ML
     0x636145534C61746ELLU, // ca_Latn_ES
     0x9C424E474C61746ELLU, // cch_Latn_NG
-    0xBC42494E42656E67LLU, // ccp_Beng_IN
     0xBC42424443616B6DLLU, // ccp_Cakm_BD
     0x636552554379726CLLU, // ce_Cyrl_RU
     0x848250484C61746ELLU, // ceb_Latn_PH
@@ -1560,10 +1587,12 @@
     0x81224B4841726162LLU, // cja_Arab_KH
     0xB122564E4368616DLLU, // cjm_Cham_VN
     0x8542495141726162LLU, // ckb_Arab_IQ
+    0x99824D4E536F796FLLU, // cmg_Soyo_MN
     0x636F46524C61746ELLU, // co_Latn_FR
     0xBDC24547436F7074LLU, // cop_Copt_EG
     0xC9E250484C61746ELLU, // cps_Latn_PH
     0x6372434143616E73LLU, // cr_Cans_CA
+    0x9E2255414379726CLLU, // crh_Cyrl_UA
     0xA622434143616E73LLU, // crj_Cans_CA
     0xAA22434143616E73LLU, // crk_Cans_CA
     0xAE22434143616E73LLU, // crl_Cans_CA
@@ -1610,6 +1639,7 @@
     0x657345534C61746ELLU, // es_Latn_ES
     0x65734D584C61746ELLU, // es_Latn_MX
     0x657355534C61746ELLU, // es_Latn_US
+    0x9A44494E476F6E6DLLU, // esg_Gonm_IN
     0xD24455534C61746ELLU, // esu_Latn_US
     0x657445454C61746ELLU, // et_Latn_EE
     0xCE6449544974616CLLU, // ett_Ital_IT
@@ -1700,10 +1730,10 @@
     0x687548554C61746ELLU, // hu_Latn_HU
     0x6879414D41726D6ELLU, // hy_Armn_AM
     0x687A4E414C61746ELLU, // hz_Latn_NA
-    0x696146524C61746ELLU, // ia_Latn_FR
     0x80284D594C61746ELLU, // iba_Latn_MY
     0x84284E474C61746ELLU, // ibb_Latn_NG
     0x696449444C61746ELLU, // id_Latn_ID
+    0x90A854474C61746ELLU, // ife_Latn_TG
     0x69674E474C61746ELLU, // ig_Latn_NG
     0x6969434E59696969LLU, // ii_Yiii_CN
     0x696B55534C61746ELLU, // ik_Latn_US
@@ -1764,6 +1794,7 @@
     0x6B6D4B484B686D72LLU, // km_Khmr_KH
     0x858A414F4C61746ELLU, // kmb_Latn_AO
     0x6B6E494E4B6E6461LLU, // kn_Knda_IN
+    0x95AA47574C61746ELLU, // knf_Latn_GW
     0x6B6F4B524B6F7265LLU, // ko_Kore_KR
     0xA1CA52554379726CLLU, // koi_Cyrl_RU
     0xA9CA494E44657661LLU, // kok_Deva_IN
@@ -1854,6 +1885,7 @@
     0x6D694E5A4C61746ELLU, // mi_Latn_NZ
     0xB50C49444C61746ELLU, // min_Latn_ID
     0xC90C495148617472LLU, // mis_Hatr_IQ
+    0xC90C4E474D656466LLU, // mis_Medf_NG
     0x6D6B4D4B4379726CLLU, // mk_Cyrl_MK
     0x6D6C494E4D6C796DLLU, // ml_Mlym_IN
     0xC96C53444C61746ELLU, // mls_Latn_SD
@@ -1877,6 +1909,7 @@
     0xAACC4D4C4C61746ELLU, // mwk_Latn_ML
     0xC6CC494E44657661LLU, // mwr_Deva_IN
     0xD6CC49444C61746ELLU, // mwv_Latn_ID
+    0xDACC5553486D6E70LLU, // mww_Hmnp_US
     0x8AEC5A574C61746ELLU, // mxc_Latn_ZW
     0x6D794D4D4D796D72LLU, // my_Mymr_MM
     0xD70C52554379726CLLU, // myv_Cyrl_RU
@@ -1905,6 +1938,7 @@
     0x998D434D4C61746ELLU, // nmg_Latn_CM
     0x6E6E4E4F4C61746ELLU, // nn_Latn_NO
     0x9DAD434D4C61746ELLU, // nnh_Latn_CM
+    0xBDAD494E5763686FLLU, // nnp_Wcho_IN
     0x6E6F4E4F4C61746ELLU, // no_Latn_NO
     0x8DCD54484C616E61LLU, // nod_Lana_TH
     0x91CD494E44657661LLU, // noe_Deva_IN
@@ -1959,6 +1993,7 @@
     0x945152454C61746ELLU, // rcf_Latn_RE
     0xA49149444C61746ELLU, // rej_Latn_ID
     0xB4D149544C61746ELLU, // rgn_Latn_IT
+    0x98F14D4D41726162LLU, // rhg_Arab_MM
     0x8111494E4C61746ELLU, // ria_Latn_IN
     0x95114D4154666E67LLU, // rif_Tfng_MA
     0xC9314E5044657661LLU, // rjs_Deva_NP
@@ -1986,6 +2021,7 @@
     0xC0124B454C61746ELLU, // saq_Latn_KE
     0xC81249444C61746ELLU, // sas_Latn_ID
     0xCC12494E4C61746ELLU, // sat_Latn_IN
+    0xD412534E4C61746ELLU, // sav_Latn_SN
     0xE412494E53617572LLU, // saz_Saur_IN
     0xBC32545A4C61746ELLU, // sbp_Latn_TZ
     0x736349544C61746ELLU, // sc_Latn_IT
@@ -2025,6 +2061,7 @@
     0x736E5A574C61746ELLU, // sn_Latn_ZW
     0xA9B24D4C4C61746ELLU, // snk_Latn_ML
     0x736F534F4C61746ELLU, // so_Latn_SO
+    0x99D2555A536F6764LLU, // sog_Sogd_UZ
     0xD1D2544854686169LLU, // sou_Thai_TH
     0x7371414C4C61746ELLU, // sq_Latn_AL
     0x737252534379726CLLU, // sr_Cyrl_RS
@@ -2135,6 +2172,7 @@
     0xC97657464C61746ELLU, // wls_Latn_WF
     0xA1B64B4D41726162LLU, // wni_Arab_KM
     0x776F534E4C61746ELLU, // wo_Latn_SN
+    0x9A56494E476F6E67LLU, // wsg_Gong_IN
     0xB276494E44657661LLU, // wtm_Deva_IN
     0xD296434E48616E73LLU, // wuu_Hans_CN
     0xD41742524C61746ELLU, // xav_Latn_BR
@@ -2169,6 +2207,7 @@
     0x7A68545748616E62LLU, // zh_Hanb_TW
     0x7A68434E48616E73LLU, // zh_Hans_CN
     0x7A68545748616E74LLU, // zh_Hant_TW
+    0xDCF9434E4E736875LLU, // zhx_Nshu_CN
     0xB17954474C61746ELLU, // zlm_Latn_TG
     0xA1994D594C61746ELLU, // zmi_Latn_MY
     0x7A755A414C61746ELLU, // zu_Latn_ZA
@@ -2194,7 +2233,7 @@
     {0x656E4154u, 0x656E80A1u}, // en-AT -> en-150
     {0x656E4155u, 0x656E8400u}, // en-AU -> en-001
     {0x656E4242u, 0x656E8400u}, // en-BB -> en-001
-    {0x656E4245u, 0x656E8400u}, // en-BE -> en-001
+    {0x656E4245u, 0x656E80A1u}, // en-BE -> en-150
     {0x656E424Du, 0x656E8400u}, // en-BM -> en-001
     {0x656E4253u, 0x656E8400u}, // en-BS -> en-001
     {0x656E4257u, 0x656E8400u}, // en-BW -> en-001
@@ -2285,6 +2324,7 @@
     {0x65734152u, 0x6573A424u}, // es-AR -> es-419
     {0x6573424Fu, 0x6573A424u}, // es-BO -> es-419
     {0x65734252u, 0x6573A424u}, // es-BR -> es-419
+    {0x6573425Au, 0x6573A424u}, // es-BZ -> es-419
     {0x6573434Cu, 0x6573A424u}, // es-CL -> es-419
     {0x6573434Fu, 0x6573A424u}, // es-CO -> es-419
     {0x65734352u, 0x6573A424u}, // es-CR -> es-419
@@ -2315,6 +2355,10 @@
     {0x7074544Cu, 0x70745054u}, // pt-TL -> pt-PT
 });
 
+const std::unordered_map<uint32_t, uint32_t> ___B_PARENTS({
+    {0x61725842u, 0x61729420u}, // ar-XB -> ar-015
+});
+
 const struct {
     const char script[4];
     const std::unordered_map<uint32_t, uint32_t>* map;
@@ -2322,6 +2366,7 @@
     {{'A', 'r', 'a', 'b'}, &ARAB_PARENTS},
     {{'H', 'a', 'n', 't'}, &HANT_PARENTS},
     {{'L', 'a', 't', 'n'}, &LATN_PARENTS},
+    {{'~', '~', '~', 'B'}, &___B_PARENTS},
 };
 
 const size_t MAX_PARENT_DEPTH = 3;
diff --git a/media/Android.bp b/media/Android.bp
index e2bdad1..75ccb22 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -77,20 +77,13 @@
     path: "apex/java"
 }
 
-metalava_updatable_media_args = " --error UnhiddenSystemApi " +
-    "--hide RequiresPermission " +
-    "--hide MissingPermission --hide BroadcastBehavior " +
-    "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
-    "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
-    "--hide HiddenTypedefConstant --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) "
-
 droidstubs {
     name: "updatable-media-stubs",
     srcs: [
         ":updatable-media-srcs",
         ":framework-media-annotation-srcs",
     ],
-    args: metalava_updatable_media_args,
+    defaults: [ "framework-module-stubs-defaults-systemapi" ],
     aidl: {
         // TODO(b/135922046) remove this
         include_dirs: ["frameworks/base/core/java"],
@@ -109,3 +102,67 @@
     srcs: [":framework-media-annotation-srcs"],
     installable: false,
 }
+
+aidl_interface {
+    name: "audio_common-aidl",
+    local_include_dir: "java",
+    srcs: [
+        "java/android/media/audio/common/AudioChannelMask.aidl",
+        "java/android/media/audio/common/AudioConfig.aidl",
+        "java/android/media/audio/common/AudioFormat.aidl",
+        "java/android/media/audio/common/AudioOffloadInfo.aidl",
+        "java/android/media/audio/common/AudioStreamType.aidl",
+        "java/android/media/audio/common/AudioUsage.aidl",
+    ],
+    backend:
+    {
+        cpp: {
+            enabled: true,
+        },
+        java: {
+            // Already generated as part of the entire media java library.
+            enabled: false,
+        },
+    },
+}
+
+aidl_interface {
+    name: "soundtrigger_middleware-aidl",
+    local_include_dir: "java",
+    srcs: [
+        "java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl",
+        "java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl",
+        "java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl",
+        "java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl",
+        "java/android/media/soundtrigger_middleware/ModelParameter.aidl",
+        "java/android/media/soundtrigger_middleware/ModelParameterRange.aidl",
+        "java/android/media/soundtrigger_middleware/Phrase.aidl",
+        "java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl",
+        "java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl",
+        "java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl",
+        "java/android/media/soundtrigger_middleware/RecognitionConfig.aidl",
+        "java/android/media/soundtrigger_middleware/RecognitionEvent.aidl",
+        "java/android/media/soundtrigger_middleware/RecognitionMode.aidl",
+        "java/android/media/soundtrigger_middleware/RecognitionStatus.aidl",
+        "java/android/media/soundtrigger_middleware/SoundModel.aidl",
+        "java/android/media/soundtrigger_middleware/SoundModelType.aidl",
+        "java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl",
+        "java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl",
+        "java/android/media/soundtrigger_middleware/Status.aidl",
+    ],
+    backend:
+    {
+        cpp: {
+            enabled: true,
+        },
+        java: {
+            // Already generated as part of the entire media java library.
+            enabled: false,
+        },
+        ndk: {
+            // Not currently needed, and disabled because of b/146172425
+            enabled: false,
+        },
+    },
+    imports: [ "audio_common-aidl" ],
+}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index df799fd..33ec46a 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -903,6 +903,18 @@
         }
     }
 
+    /**
+     * Returns a human readable name for a given device type
+     * @param device a native device type, NOT an AudioDeviceInfo type
+     * @return a string describing the device type
+     */
+    public static @NonNull String getDeviceName(int device) {
+        if ((device & DEVICE_BIT_IN) != 0) {
+            return getInputDeviceName(device);
+        }
+        return getOutputDeviceName(device);
+    }
+
     // phone state, match audio_mode???
     public static final int PHONE_STATE_OFFCALL = 0;
     public static final int PHONE_STATE_RINGING = 1;
diff --git a/media/java/android/media/audio/common/AudioChannelMask.aidl b/media/java/android/media/audio/common/AudioChannelMask.aidl
new file mode 100644
index 0000000..b9b08e6
--- /dev/null
+++ b/media/java/android/media/audio/common/AudioChannelMask.aidl
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ // This file has been semi-automatically generated using hidl2aidl from its counterpart in
+ // hardware/interfaces/audio/common/5.0/types.hal
+
+package android.media.audio.common;
+
+/**
+ * A channel mask per se only defines the presence or absence of a channel, not
+ * the order.
+ *
+ * The channel order convention is that channels are interleaved in order from
+ * least significant channel mask bit to most significant channel mask bit,
+ * with unused bits skipped. For example for stereo, LEFT would be first,
+ * followed by RIGHT.
+ * Any exceptions to this convention are noted at the appropriate API.
+ *
+ * AudioChannelMask is an opaque type and its internal layout should not be
+ * assumed as it may change in the future.  Instead, always use functions
+ * to examine it.
+ *
+ * These are the current representations:
+ *
+ *   REPRESENTATION_POSITION
+ *     is a channel mask representation for position assignment.  Each low-order
+ *     bit corresponds to the spatial position of a transducer (output), or
+ *     interpretation of channel (input).  The user of a channel mask needs to
+ *     know the context of whether it is for output or input.  The constants
+ *     OUT_* or IN_* apply to the bits portion.  It is not permitted for no bits
+ *     to be set.
+ *
+ *   REPRESENTATION_INDEX
+ *     is a channel mask representation for index assignment.  Each low-order
+ *     bit corresponds to a selected channel.  There is no platform
+ *     interpretation of the various bits.  There is no concept of output or
+ *     input.  It is not permitted for no bits to be set.
+ *
+ * All other representations are reserved for future use.
+ *
+ * Warning: current representation distinguishes between input and output, but
+ * this will not the be case in future revisions of the platform. Wherever there
+ * is an ambiguity between input and output that is currently resolved by
+ * checking the channel mask, the implementer should look for ways to fix it
+ * with additional information outside of the mask.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioChannelMask {
+    /**
+     * must be 0 for compatibility
+     */
+    REPRESENTATION_POSITION = 0,
+    /**
+     * 1 is reserved for future use
+     */
+    REPRESENTATION_INDEX = 2,
+    /**
+     * 3 is reserved for future use
+     *
+     *
+     * These can be a complete value of AudioChannelMask
+     */
+    NONE = 0x0,
+    INVALID = 0xC0000000,
+    /**
+     * These can be the bits portion of an AudioChannelMask
+     * with representation REPRESENTATION_POSITION.
+     *
+     *
+     * output channels
+     */
+    OUT_FRONT_LEFT = 0x1,
+    OUT_FRONT_RIGHT = 0x2,
+    OUT_FRONT_CENTER = 0x4,
+    OUT_LOW_FREQUENCY = 0x8,
+    OUT_BACK_LEFT = 0x10,
+    OUT_BACK_RIGHT = 0x20,
+    OUT_FRONT_LEFT_OF_CENTER = 0x40,
+    OUT_FRONT_RIGHT_OF_CENTER = 0x80,
+    OUT_BACK_CENTER = 0x100,
+    OUT_SIDE_LEFT = 0x200,
+    OUT_SIDE_RIGHT = 0x400,
+    OUT_TOP_CENTER = 0x800,
+    OUT_TOP_FRONT_LEFT = 0x1000,
+    OUT_TOP_FRONT_CENTER = 0x2000,
+    OUT_TOP_FRONT_RIGHT = 0x4000,
+    OUT_TOP_BACK_LEFT = 0x8000,
+    OUT_TOP_BACK_CENTER = 0x10000,
+    OUT_TOP_BACK_RIGHT = 0x20000,
+    OUT_TOP_SIDE_LEFT = 0x40000,
+    OUT_TOP_SIDE_RIGHT = 0x80000,
+    /**
+     * Haptic channel characteristics are specific to a device and
+     * only used to play device specific resources (eg: ringtones).
+     * The HAL can freely map A and B to haptic controllers, the
+     * framework shall not interpret those values and forward them
+     * from the device audio assets.
+     */
+    OUT_HAPTIC_A = 0x20000000,
+    OUT_HAPTIC_B = 0x10000000,
+// TODO(ytai): Aliases not currently supported in AIDL - can inline the values.
+//    OUT_MONO = OUT_FRONT_LEFT,
+//    OUT_STEREO = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT),
+//    OUT_2POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_LOW_FREQUENCY),
+//    OUT_2POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+//    OUT_2POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
+//    OUT_3POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+//    OUT_3POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
+//    OUT_QUAD = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_BACK_LEFT | OUT_BACK_RIGHT),
+//    OUT_QUAD_BACK = OUT_QUAD,
+//    /**
+//     * like OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_*
+//     */
+//    OUT_QUAD_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+//    OUT_SURROUND = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_BACK_CENTER),
+//    OUT_PENTA = (OUT_QUAD | OUT_FRONT_CENTER),
+//    OUT_5POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT),
+//    OUT_5POINT1_BACK = OUT_5POINT1,
+//    /**
+//     * like OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_*
+//     */
+//    OUT_5POINT1_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+//    OUT_5POINT1POINT2 = (OUT_5POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+//    OUT_5POINT1POINT4 = (OUT_5POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
+//    OUT_6POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_BACK_CENTER),
+//    /**
+//     * matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
+//     */
+//    OUT_7POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+//    OUT_7POINT1POINT2 = (OUT_7POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+//    OUT_7POINT1POINT4 = (OUT_7POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
+//    OUT_MONO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_HAPTIC_A),
+//    OUT_STEREO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A),
+//    OUT_HAPTIC_AB = (OUT_HAPTIC_A | OUT_HAPTIC_B),
+//    OUT_MONO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+//    OUT_STEREO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+    /**
+     * These are bits only, not complete values
+     *
+     *
+     * input channels
+     */
+    IN_LEFT = 0x4,
+    IN_RIGHT = 0x8,
+    IN_FRONT = 0x10,
+    IN_BACK = 0x20,
+    IN_LEFT_PROCESSED = 0x40,
+    IN_RIGHT_PROCESSED = 0x80,
+    IN_FRONT_PROCESSED = 0x100,
+    IN_BACK_PROCESSED = 0x200,
+    IN_PRESSURE = 0x400,
+    IN_X_AXIS = 0x800,
+    IN_Y_AXIS = 0x1000,
+    IN_Z_AXIS = 0x2000,
+    IN_BACK_LEFT = 0x10000,
+    IN_BACK_RIGHT = 0x20000,
+    IN_CENTER = 0x40000,
+    IN_LOW_FREQUENCY = 0x100000,
+    IN_TOP_LEFT = 0x200000,
+    IN_TOP_RIGHT = 0x400000,
+    IN_VOICE_UPLINK = 0x4000,
+    IN_VOICE_DNLINK = 0x8000,
+// TODO(ytai): Aliases not currently supported in AIDL - can inline the values.
+//    IN_MONO = IN_FRONT,
+//    IN_STEREO = (IN_LEFT | IN_RIGHT),
+//    IN_FRONT_BACK = (IN_FRONT | IN_BACK),
+//    IN_6 = (IN_LEFT | IN_RIGHT | IN_FRONT | IN_BACK | IN_LEFT_PROCESSED | IN_RIGHT_PROCESSED),
+//    IN_2POINT0POINT2 = (IN_LEFT | IN_RIGHT | IN_TOP_LEFT | IN_TOP_RIGHT),
+//    IN_2POINT1POINT2 = (IN_LEFT | IN_RIGHT | IN_TOP_LEFT | IN_TOP_RIGHT | IN_LOW_FREQUENCY),
+//    IN_3POINT0POINT2 = (IN_LEFT | IN_CENTER | IN_RIGHT | IN_TOP_LEFT | IN_TOP_RIGHT),
+//    IN_3POINT1POINT2 = (IN_LEFT | IN_CENTER | IN_RIGHT | IN_TOP_LEFT | IN_TOP_RIGHT | IN_LOW_FREQUENCY),
+//    IN_5POINT1 = (IN_LEFT | IN_CENTER | IN_RIGHT | IN_BACK_LEFT | IN_BACK_RIGHT | IN_LOW_FREQUENCY),
+//    IN_VOICE_UPLINK_MONO = (IN_VOICE_UPLINK | IN_MONO),
+//    IN_VOICE_DNLINK_MONO = (IN_VOICE_DNLINK | IN_MONO),
+//    IN_VOICE_CALL_MONO = (IN_VOICE_UPLINK_MONO | IN_VOICE_DNLINK_MONO),
+//    COUNT_MAX = 30,
+//    INDEX_HDR = REPRESENTATION_INDEX << COUNT_MAX,
+//    INDEX_MASK_1 = INDEX_HDR | ((1 << 1) - 1),
+//    INDEX_MASK_2 = INDEX_HDR | ((1 << 2) - 1),
+//    INDEX_MASK_3 = INDEX_HDR | ((1 << 3) - 1),
+//    INDEX_MASK_4 = INDEX_HDR | ((1 << 4) - 1),
+//    INDEX_MASK_5 = INDEX_HDR | ((1 << 5) - 1),
+//    INDEX_MASK_6 = INDEX_HDR | ((1 << 6) - 1),
+//    INDEX_MASK_7 = INDEX_HDR | ((1 << 7) - 1),
+//    INDEX_MASK_8 = INDEX_HDR | ((1 << 8) - 1),
+//    INDEX_MASK_9 = INDEX_HDR | ((1 << 9) - 1),
+//    INDEX_MASK_10 = INDEX_HDR | ((1 << 10) - 1),
+//    INDEX_MASK_11 = INDEX_HDR | ((1 << 11) - 1),
+//    INDEX_MASK_12 = INDEX_HDR | ((1 << 12) - 1),
+//    INDEX_MASK_13 = INDEX_HDR | ((1 << 13) - 1),
+//    INDEX_MASK_14 = INDEX_HDR | ((1 << 14) - 1),
+//    INDEX_MASK_15 = INDEX_HDR | ((1 << 15) - 1),
+//    INDEX_MASK_16 = INDEX_HDR | ((1 << 16) - 1),
+//    INDEX_MASK_17 = INDEX_HDR | ((1 << 17) - 1),
+//    INDEX_MASK_18 = INDEX_HDR | ((1 << 18) - 1),
+//    INDEX_MASK_19 = INDEX_HDR | ((1 << 19) - 1),
+//    INDEX_MASK_20 = INDEX_HDR | ((1 << 20) - 1),
+//    INDEX_MASK_21 = INDEX_HDR | ((1 << 21) - 1),
+//    INDEX_MASK_22 = INDEX_HDR | ((1 << 22) - 1),
+//    INDEX_MASK_23 = INDEX_HDR | ((1 << 23) - 1),
+//    INDEX_MASK_24 = INDEX_HDR | ((1 << 24) - 1),
+}
diff --git a/media/java/android/media/audio/common/AudioConfig.aidl b/media/java/android/media/audio/common/AudioConfig.aidl
new file mode 100644
index 0000000..50dd796
--- /dev/null
+++ b/media/java/android/media/audio/common/AudioConfig.aidl
@@ -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.
+ */
+
+ // This file has been semi-automatically generated using hidl2aidl from its counterpart in
+ // hardware/interfaces/audio/common/5.0/types.hal
+
+package android.media.audio.common;
+
+import android.media.audio.common.AudioFormat;
+import android.media.audio.common.AudioOffloadInfo;
+
+/**
+ * Commonly used audio stream configuration parameters.
+ *
+ * {@hide}
+ */
+parcelable AudioConfig {
+    int sampleRateHz;
+    int channelMask;
+    AudioFormat format;
+    AudioOffloadInfo offloadInfo;
+    long frameCount;
+}
diff --git a/media/java/android/media/audio/common/AudioFormat.aidl b/media/java/android/media/audio/common/AudioFormat.aidl
new file mode 100644
index 0000000..aadc8e2
--- /dev/null
+++ b/media/java/android/media/audio/common/AudioFormat.aidl
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ // This file has been semi-automatically generated using hidl2aidl from its counterpart in
+ // hardware/interfaces/audio/common/5.0/types.hal
+
+package android.media.audio.common;
+
+/**
+ * Audio format  is a 32-bit word that consists of:
+ *   main format field (upper 8 bits)
+ *   sub format field (lower 24 bits).
+ *
+ * The main format indicates the main codec type. The sub format field indicates
+ * options and parameters for each format. The sub format is mainly used for
+ * record to indicate for instance the requested bitrate or profile.  It can
+ * also be used for certain formats to give informations not present in the
+ * encoded audio stream (e.g. octet alignement for AMR).
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioFormat {
+   INVALID = 0xFFFFFFFF,
+   DEFAULT = 0,
+   PCM = 0x00000000,
+   MP3 = 0x01000000,
+   AMR_NB = 0x02000000,
+   AMR_WB = 0x03000000,
+   AAC = 0x04000000,
+   /**
+    * Deprecated, Use AAC_HE_V1
+    */
+   HE_AAC_V1 = 0x05000000,
+   /**
+    * Deprecated, Use AAC_HE_V2
+    */
+   HE_AAC_V2 = 0x06000000,
+   VORBIS = 0x07000000,
+   OPUS = 0x08000000,
+   AC3 = 0x09000000,
+   E_AC3 = 0x0A000000,
+   DTS = 0x0B000000,
+   DTS_HD = 0x0C000000,
+   /**
+    * IEC61937 is encoded audio wrapped in 16-bit PCM.
+    */
+   IEC61937 = 0x0D000000,
+   DOLBY_TRUEHD = 0x0E000000,
+   EVRC = 0x10000000,
+   EVRCB = 0x11000000,
+   EVRCWB = 0x12000000,
+   EVRCNW = 0x13000000,
+   AAC_ADIF = 0x14000000,
+   WMA = 0x15000000,
+   WMA_PRO = 0x16000000,
+   AMR_WB_PLUS = 0x17000000,
+   MP2 = 0x18000000,
+   QCELP = 0x19000000,
+   DSD = 0x1A000000,
+   FLAC = 0x1B000000,
+   ALAC = 0x1C000000,
+   APE = 0x1D000000,
+   AAC_ADTS = 0x1E000000,
+   SBC = 0x1F000000,
+   APTX = 0x20000000,
+   APTX_HD = 0x21000000,
+   AC4 = 0x22000000,
+   LDAC = 0x23000000,
+   /**
+    * Dolby Metadata-enhanced Audio Transmission
+    */
+   MAT = 0x24000000,
+   AAC_LATM = 0x25000000,
+   CELT = 0x26000000,
+   APTX_ADAPTIVE = 0x27000000,
+   LHDC = 0x28000000,
+   LHDC_LL = 0x29000000,
+   APTX_TWSP = 0x2A000000,
+   /**
+    * Deprecated
+    */
+   MAIN_MASK = 0xFF000000,
+   SUB_MASK = 0x00FFFFFF,
+   /**
+    * Subformats
+    */
+   PCM_SUB_16_BIT = 0x1,
+   PCM_SUB_8_BIT = 0x2,
+   PCM_SUB_32_BIT = 0x3,
+   PCM_SUB_8_24_BIT = 0x4,
+   PCM_SUB_FLOAT = 0x5,
+   PCM_SUB_24_BIT_PACKED = 0x6,
+   MP3_SUB_NONE = 0x0,
+   AMR_SUB_NONE = 0x0,
+   AAC_SUB_MAIN = 0x1,
+   AAC_SUB_LC = 0x2,
+   AAC_SUB_SSR = 0x4,
+   AAC_SUB_LTP = 0x8,
+   AAC_SUB_HE_V1 = 0x10,
+   AAC_SUB_SCALABLE = 0x20,
+   AAC_SUB_ERLC = 0x40,
+   AAC_SUB_LD = 0x80,
+   AAC_SUB_HE_V2 = 0x100,
+   AAC_SUB_ELD = 0x200,
+   AAC_SUB_XHE = 0x300,
+   VORBIS_SUB_NONE = 0x0,
+   E_AC3_SUB_JOC = 0x1,
+   MAT_SUB_1_0 = 0x1,
+   MAT_SUB_2_0 = 0x2,
+   MAT_SUB_2_1 = 0x3,
+// TODO(ytai): Aliases not currently supported in AIDL - can inline the values.
+//   /**
+//    * Aliases
+//    *
+//    *
+//    * note != AudioFormat.ENCODING_PCM_16BIT
+//    */
+//   PCM_16_BIT = (PCM | PCM_SUB_16_BIT),
+//   /**
+//    * note != AudioFormat.ENCODING_PCM_8BIT
+//    */
+//   PCM_8_BIT = (PCM | PCM_SUB_8_BIT),
+//   PCM_32_BIT = (PCM | PCM_SUB_32_BIT),
+//   PCM_8_24_BIT = (PCM | PCM_SUB_8_24_BIT),
+//   PCM_FLOAT = (PCM | PCM_SUB_FLOAT),
+//   PCM_24_BIT_PACKED = (PCM | PCM_SUB_24_BIT_PACKED),
+//   AAC_MAIN = (AAC | AAC_SUB_MAIN),
+//   AAC_LC = (AAC | AAC_SUB_LC),
+//   AAC_SSR = (AAC | AAC_SUB_SSR),
+//   AAC_LTP = (AAC | AAC_SUB_LTP),
+//   AAC_HE_V1 = (AAC | AAC_SUB_HE_V1),
+//   AAC_SCALABLE = (AAC | AAC_SUB_SCALABLE),
+//   AAC_ERLC = (AAC | AAC_SUB_ERLC),
+//   AAC_LD = (AAC | AAC_SUB_LD),
+//   AAC_HE_V2 = (AAC | AAC_SUB_HE_V2),
+//   AAC_ELD = (AAC | AAC_SUB_ELD),
+//   AAC_XHE = (AAC | AAC_SUB_XHE),
+//   AAC_ADTS_MAIN = (AAC_ADTS | AAC_SUB_MAIN),
+//   AAC_ADTS_LC = (AAC_ADTS | AAC_SUB_LC),
+//   AAC_ADTS_SSR = (AAC_ADTS | AAC_SUB_SSR),
+//   AAC_ADTS_LTP = (AAC_ADTS | AAC_SUB_LTP),
+//   AAC_ADTS_HE_V1 = (AAC_ADTS | AAC_SUB_HE_V1),
+//   AAC_ADTS_SCALABLE = (AAC_ADTS | AAC_SUB_SCALABLE),
+//   AAC_ADTS_ERLC = (AAC_ADTS | AAC_SUB_ERLC),
+//   AAC_ADTS_LD = (AAC_ADTS | AAC_SUB_LD),
+//   AAC_ADTS_HE_V2 = (AAC_ADTS | AAC_SUB_HE_V2),
+//   AAC_ADTS_ELD = (AAC_ADTS | AAC_SUB_ELD),
+//   AAC_ADTS_XHE = (AAC_ADTS | AAC_SUB_XHE),
+//   E_AC3_JOC = (E_AC3 | E_AC3_SUB_JOC),
+//   MAT_1_0 = (MAT | MAT_SUB_1_0),
+//   MAT_2_0 = (MAT | MAT_SUB_2_0),
+//   MAT_2_1 = (MAT | MAT_SUB_2_1),
+//   AAC_LATM_LC = (AAC_LATM | AAC_SUB_LC),
+//   AAC_LATM_HE_V1 = (AAC_LATM | AAC_SUB_HE_V1),
+//   AAC_LATM_HE_V2 = (AAC_LATM | AAC_SUB_HE_V2),
+}
diff --git a/media/java/android/media/audio/common/AudioOffloadInfo.aidl b/media/java/android/media/audio/common/AudioOffloadInfo.aidl
new file mode 100644
index 0000000..ec10d71
--- /dev/null
+++ b/media/java/android/media/audio/common/AudioOffloadInfo.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ // This file has been semi-automatically generated using hidl2aidl from its counterpart in
+ // hardware/interfaces/audio/common/5.0/types.hal
+
+package android.media.audio.common;
+
+import android.media.audio.common.AudioFormat;
+import android.media.audio.common.AudioStreamType;
+import android.media.audio.common.AudioUsage;
+
+/**
+ * Additional information about the stream passed to hardware decoders.
+ *
+ * {@hide}
+ */
+parcelable AudioOffloadInfo {
+    int sampleRateHz;
+    int channelMask;
+    AudioFormat format;
+    AudioStreamType streamType;
+    int bitRatePerSecond;
+    long durationMicroseconds;
+    boolean hasVideo;
+    boolean isStreaming;
+    int bitWidth;
+    int bufferSize;
+    AudioUsage usage;
+}
+
diff --git a/media/java/android/media/audio/common/AudioStreamType.aidl b/media/java/android/media/audio/common/AudioStreamType.aidl
new file mode 100644
index 0000000..c545667
--- /dev/null
+++ b/media/java/android/media/audio/common/AudioStreamType.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ // This file has been semi-automatically generated using hidl2aidl from its counterpart in
+ // hardware/interfaces/audio/common/5.0/types.hal
+
+package android.media.audio.common;
+
+/**
+ *  Audio streams
+ *
+ * Audio stream type describing the intended use case of a stream.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioStreamType {
+    DEFAULT = -1,
+    MIN = 0,
+    VOICE_CALL = 0,
+    SYSTEM = 1,
+    RING = 2,
+    MUSIC = 3,
+    ALARM = 4,
+    NOTIFICATION = 5,
+    BLUETOOTH_SCO = 6,
+    ENFORCED_AUDIBLE = 7,
+    DTMF = 8,
+    TTS = 9,
+    ACCESSIBILITY = 10,
+}
diff --git a/media/java/android/media/audio/common/AudioUsage.aidl b/media/java/android/media/audio/common/AudioUsage.aidl
new file mode 100644
index 0000000..ef34816
--- /dev/null
+++ b/media/java/android/media/audio/common/AudioUsage.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ // This file has been semi-automatically generated using hidl2aidl from its counterpart in
+ // hardware/interfaces/audio/common/5.0/types.hal
+
+package android.media.audio.common;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum AudioUsage {
+    UNKNOWN = 0,
+    MEDIA = 1,
+    VOICE_COMMUNICATION = 2,
+    VOICE_COMMUNICATION_SIGNALLING = 3,
+    ALARM = 4,
+    NOTIFICATION = 5,
+    NOTIFICATION_TELEPHONY_RINGTONE = 6,
+    ASSISTANCE_ACCESSIBILITY = 11,
+    ASSISTANCE_NAVIGATION_GUIDANCE = 12,
+    ASSISTANCE_SONIFICATION = 13,
+    GAME = 14,
+    VIRTUAL_SOURCE = 15,
+    ASSISTANT = 16,
+}
diff --git a/media/java/android/media/session/ICallback.aidl b/media/java/android/media/session/ICallback.aidl
deleted file mode 100644
index 322bffa..0000000
--- a/media/java/android/media/session/ICallback.aidl
+++ /dev/null
@@ -1,35 +0,0 @@
-/* Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.session;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.media.session.MediaSession;
-import android.view.KeyEvent;
-
-/**
- * @hide
- */
-oneway interface ICallback {
-    void onMediaKeyEventDispatchedToMediaSession(in KeyEvent event,
-            in MediaSession.Token sessionToken);
-    void onMediaKeyEventDispatchedToMediaButtonReceiver(in KeyEvent event,
-            in ComponentName mediaButtonReceiver);
-
-    void onAddressedPlayerChangedToMediaSession(in MediaSession.Token sessionToken);
-    void onAddressedPlayerChangedToMediaButtonReceiver(in ComponentName mediaButtonReceiver);
-}
-
diff --git a/media/java/android/media/session/IOnMediaKeyEventDispatchedListener.aidl b/media/java/android/media/session/IOnMediaKeyEventDispatchedListener.aidl
new file mode 100644
index 0000000..90d9134
--- /dev/null
+++ b/media/java/android/media/session/IOnMediaKeyEventDispatchedListener.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright 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.media.session;
+
+import android.media.session.MediaSession;
+import android.view.KeyEvent;
+
+/**
+ * @hide
+ */
+oneway interface IOnMediaKeyEventDispatchedListener {
+    void onMediaKeyEventDispatched(in KeyEvent event, in String packageName,
+            in MediaSession.Token sessionToken);
+}
diff --git a/media/java/android/media/session/IOnMediaKeyEventSessionChangedListener.aidl b/media/java/android/media/session/IOnMediaKeyEventSessionChangedListener.aidl
new file mode 100644
index 0000000..9566e75
--- /dev/null
+++ b/media/java/android/media/session/IOnMediaKeyEventSessionChangedListener.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright 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.media.session;
+
+import android.media.session.MediaSession;
+
+/**
+ * @hide
+ */
+oneway interface IOnMediaKeyEventSessionChangedListener {
+    void onMediaKeyEventSessionChanged(in String packageName,
+            in MediaSession.Token mediaKeyEventSessionToken);
+}
+
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 01e6ed5..c8502a5 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -20,7 +20,8 @@
 import android.media.IRemoteVolumeController;
 import android.media.Session2Token;
 import android.media.session.IActiveSessionsListener;
-import android.media.session.ICallback;
+import android.media.session.IOnMediaKeyEventDispatchedListener;
+import android.media.session.IOnMediaKeyEventSessionChangedListener;
 import android.media.session.IOnMediaKeyListener;
 import android.media.session.IOnVolumeKeyLongPressListener;
 import android.media.session.ISession;
@@ -62,8 +63,12 @@
     // For PhoneWindowManager to precheck media keys
     boolean isGlobalPriorityActive();
 
-    void registerCallback(in ICallback callback);
-    void unregisterCallback(in ICallback callback);
+    void addOnMediaKeyEventDispatchedListener(in IOnMediaKeyEventDispatchedListener listener);
+    void removeOnMediaKeyEventDispatchedListener(in IOnMediaKeyEventDispatchedListener listener);
+    void addOnMediaKeyEventSessionChangedListener(
+            in IOnMediaKeyEventSessionChangedListener listener);
+    void removeOnMediaKeyEventSessionChangedListener(
+            in IOnMediaKeyEventSessionChangedListener listener);
     void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener);
     void setOnMediaKeyListener(in IOnMediaKeyListener listener);
 
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 92fb31b..a89dc5f 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -32,7 +32,6 @@
 import android.media.Session2Token;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -78,29 +77,33 @@
      */
     public static final int RESULT_MEDIA_KEY_HANDLED = 1;
     private final ISessionManager mService;
+    private final OnMediaKeyEventDispatchedListenerStub mOnMediaKeyEventDispatchedListenerStub =
+            new OnMediaKeyEventDispatchedListenerStub();
+    private final OnMediaKeyEventSessionChangedListenerStub
+            mOnMediaKeyEventSessionChangedListenerStub =
+            new OnMediaKeyEventSessionChangedListenerStub();
 
     private final Object mLock = new Object();
     @GuardedBy("mLock")
-    private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners
-            = new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>();
+    private final ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper> mListeners =
+            new ArrayMap<OnActiveSessionsChangedListener, SessionsChangedWrapper>();
     @GuardedBy("mLock")
     private final ArrayMap<OnSession2TokensChangedListener, Session2TokensChangedWrapper>
             mSession2TokensListeners = new ArrayMap<>();
     @GuardedBy("mLock")
-    private final CallbackStub mCbStub = new CallbackStub();
+    private final Map<OnMediaKeyEventDispatchedListener, Executor>
+            mOnMediaKeyEventDispatchedListeners = new HashMap<>();
     @GuardedBy("mLock")
-    private final Map<Callback, Executor> mCallbacks = new HashMap<>();
+    private final Map<OnMediaKeyEventSessionChangedListener, Executor>
+            mMediaKeyEventSessionChangedCallbacks = new HashMap<>();
     @GuardedBy("mLock")
-    private MediaSession.Token mCurMediaButtonSession;
+    private String mCurMediaKeyEventSessionPackage;
     @GuardedBy("mLock")
-    private ComponentName mCurMediaButtonReceiver;
+    private MediaSession.Token mCurMediaKeyEventSession;
 
     private Context mContext;
     private OnVolumeKeyLongPressListenerImpl mOnVolumeKeyLongPressListener;
     private OnMediaKeyListenerImpl mOnMediaKeyListener;
-    // TODO: Remove mLegacyCallback once Bluetooth app stop calling setCallback() method.
-    @GuardedBy("mLock")
-    private Callback mLegacyCallback;
 
     /**
      * @hide
@@ -756,89 +759,118 @@
     }
 
     /**
-     * Set a {@link Callback}.
-     *
-     * <p>System can only have a single callback, and the callback can only be set by
-     * Bluetooth service process.
-     *
-     * @param callback A {@link Callback}. {@code null} to reset.
-     * @param handler The handler on which the callback should be invoked, or {@code null}
-     *            if the callback should be invoked on the calling thread's looper.
-     * @hide
-     */
-    // TODO: Remove this method once Bluetooth app stop calling it.
-    public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
-        if (handler == null) {
-            handler = new Handler();
-        }
-        synchronized (mLock) {
-            if (mLegacyCallback != null) {
-                unregisterCallback(mLegacyCallback);
-            }
-            mLegacyCallback = callback;
-            if (callback != null) {
-                registerCallback(new HandlerExecutor(handler), callback);
-            }
-        }
-    }
-
-    /**
-     * Register a {@link Callback}.
+     * Add a {@link OnMediaKeyEventDispatchedListener}.
      *
      * @param executor The executor on which the callback should be invoked
-     * @param callback A {@link Callback}.
+     * @param listener A {@link OnMediaKeyEventDispatchedListener}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL)
-    public void registerCallback(@NonNull @CallbackExecutor Executor executor,
-            @NonNull Callback callback) {
+    public void addOnMediaKeyEventDispatchedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnMediaKeyEventDispatchedListener listener) {
         if (executor == null) {
             throw new NullPointerException("executor shouldn't be null");
         }
-        if (callback == null) {
-            throw new NullPointerException("callback shouldn't be null");
+        if (listener == null) {
+            throw new NullPointerException("listener shouldn't be null");
         }
         synchronized (mLock) {
             try {
-                mCallbacks.put(callback, executor);
-                if (mCurMediaButtonSession != null) {
-                    executor.execute(
-                            () -> callback.onAddressedPlayerChanged(mCurMediaButtonSession));
-                } else if (mCurMediaButtonReceiver != null) {
-                    executor.execute(
-                            () -> callback.onAddressedPlayerChanged(mCurMediaButtonReceiver));
-                }
-
-                if (mCallbacks.size() == 1) {
-                    mService.registerCallback(mCbStub);
+                mOnMediaKeyEventDispatchedListeners.put(listener, executor);
+                if (mOnMediaKeyEventDispatchedListeners.size() == 1) {
+                    mService.addOnMediaKeyEventDispatchedListener(
+                            mOnMediaKeyEventDispatchedListenerStub);
                 }
             } catch (RemoteException e) {
-                Log.e(TAG, "Failed to set media key callback", e);
+                Log.e(TAG, "Failed to set media key listener", e);
             }
         }
     }
 
     /**
-     * Unregister a {@link Callback}.
+     * Remove a {@link OnMediaKeyEventDispatchedListener}.
      *
-     * @param callback A {@link Callback}.
+     * @param listener A {@link OnMediaKeyEventDispatchedListener}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL)
-    public void unregisterCallback(@NonNull Callback callback) {
-        if (callback == null) {
-            throw new NullPointerException("callback shouldn't be null");
+    public void removeOnMediaKeyEventDispatchedListener(
+            @NonNull OnMediaKeyEventDispatchedListener listener) {
+        if (listener == null) {
+            throw new NullPointerException("listener shouldn't be null");
         }
         synchronized (mLock) {
             try {
-                mCallbacks.remove(callback);
-                if (mCallbacks.size() == 0) {
-                    mService.unregisterCallback(mCbStub);
+                mOnMediaKeyEventDispatchedListeners.remove(listener);
+                if (mOnMediaKeyEventDispatchedListeners.size() == 0) {
+                    mService.removeOnMediaKeyEventDispatchedListener(
+                            mOnMediaKeyEventDispatchedListenerStub);
                 }
             } catch (RemoteException e) {
-                Log.e(TAG, "Failed to set media key callback", e);
+                Log.e(TAG, "Failed to set media key event dispatched listener", e);
+            }
+        }
+    }
+
+    /**
+     * Add a {@link OnMediaKeyEventDispatchedListener}.
+     *
+     * @param executor The executor on which the callback should be invoked
+     * @param listener A {@link OnMediaKeyEventSessionChangedListener}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL)
+    public void addOnMediaKeyEventSessionChangedListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnMediaKeyEventSessionChangedListener listener) {
+        if (executor == null) {
+            throw new NullPointerException("executor shouldn't be null");
+        }
+        if (listener == null) {
+            throw new NullPointerException("listener shouldn't be null");
+        }
+        synchronized (mLock) {
+            try {
+                mMediaKeyEventSessionChangedCallbacks.put(listener, executor);
+                executor.execute(
+                        () -> listener.onMediaKeyEventSessionChanged(
+                                mCurMediaKeyEventSessionPackage, mCurMediaKeyEventSession));
+                if (mMediaKeyEventSessionChangedCallbacks.size() == 1) {
+                    mService.addOnMediaKeyEventSessionChangedListener(
+                            mOnMediaKeyEventSessionChangedListenerStub);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to set media key listener", e);
+            }
+        }
+    }
+
+    /**
+     * Remove a {@link OnMediaKeyEventSessionChangedListener}.
+     *
+     * @param listener A {@link OnMediaKeyEventSessionChangedListener}.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL)
+    public void removeOnMediaKeyEventSessionChangedListener(
+            @NonNull OnMediaKeyEventSessionChangedListener listener) {
+        if (listener == null) {
+            throw new NullPointerException("listener shouldn't be null");
+        }
+        synchronized (mLock) {
+            try {
+                mMediaKeyEventSessionChangedCallbacks.remove(listener);
+                if (mMediaKeyEventSessionChangedCallbacks.size() == 0) {
+                    mService.removeOnMediaKeyEventSessionChangedListener(
+                            mOnMediaKeyEventSessionChangedListenerStub);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to set media key listener", e);
             }
         }
     }
@@ -900,54 +932,46 @@
     }
 
     /**
-     * Callbacks for the media session service.
-     *
-     * <p>Called when a media key event is dispatched or the addressed player is changed.
-     * The addressed player is either the media session or the media button receiver that will
-     * receive media key events.
+     * Listener to receive when the media session service
      * @hide
      */
     @SystemApi
-    public static abstract class Callback {
+    public interface OnMediaKeyEventDispatchedListener {
         /**
-         * Called when a media key event is dispatched to the media session
-         * through the media session service.
+         * Called when a media key event is dispatched through the media session service. The
+         * session token can be {@link null} if the framework has sent the media key event to the
+         * media button receiver to revive the media app's playback.
+         *
+         * the session is dead when , but the framework sent
          *
          * @param event Dispatched media key event.
-         * @param sessionToken The media session's token.
+         * @param packageName Package
+         * @param sessionToken The media session's token. Can be {@code null}.
          */
-        public abstract void onMediaKeyEventDispatched(KeyEvent event,
-                MediaSession.Token sessionToken);
+        default void onMediaKeyEventDispatched(@NonNull KeyEvent event, @NonNull String packageName,
+                @NonNull MediaSession.Token sessionToken) { }
+    }
 
+    /**
+     * Listener to receive changes in the media key event session, which would receive the media key
+     * event unless specified.
+     * @hide
+     */
+    @SystemApi
+    public interface OnMediaKeyEventSessionChangedListener {
         /**
-         * Called when a media key event is dispatched to the media button receiver
-         * through the media session service.
-         * <p>MediaSessionService may broadcast key events to the media button receiver
-         * when reviving playback after the media session is released.
+         * Called when the media key session is changed to the given media session. The key event
+         * session is the media session which would receive key event by default, unless the caller
+         * has specified the target.
+         * <p>
+         * The session token can be {@link null} if the media button session is unset. In that case,
+         * framework would dispatch to the last sessions's media button receiver.
          *
-         * @param event Dispatched media key event.
-         * @param mediaButtonReceiver The media button receiver.
+         * @param packageName The package name who would receive the media key event. Can be empty.
+         * @param sessionToken The media session's token. Can be {@code null.}
          */
-        public abstract void onMediaKeyEventDispatched(KeyEvent event,
-                ComponentName mediaButtonReceiver);
-
-        /**
-         * Called when the addressed player is changed to a media session.
-         * <p>One of the {@ #onAddressedPlayerChanged} will be also called immediately after
-         * {@link #registerCallback} if the addressed player exists.
-         *
-         * @param sessionToken The media session's token.
-         */
-        public abstract void onAddressedPlayerChanged(MediaSession.Token sessionToken);
-
-        /**
-         * Called when the addressed player is changed to the media button receiver.
-         * <p>One of the {@ #onAddressedPlayerChanged} will be also called immediately after
-         * {@link #registerCallback} if the addressed player exists.
-         *
-         * @param mediaButtonReceiver The media button receiver.
-         */
-        public abstract void onAddressedPlayerChanged(ComponentName mediaButtonReceiver);
+        default void onMediaKeyEventSessionChanged(@NonNull String packageName,
+                @Nullable MediaSession.Token sessionToken) { }
     }
 
     /**
@@ -1149,50 +1173,35 @@
         }
     }
 
-    private final class CallbackStub extends ICallback.Stub {
+    private final class OnMediaKeyEventDispatchedListenerStub
+            extends IOnMediaKeyEventDispatchedListener.Stub {
 
         @Override
-        public void onMediaKeyEventDispatchedToMediaSession(KeyEvent event,
+        public void onMediaKeyEventDispatched(KeyEvent event, String packageName,
                 MediaSession.Token sessionToken) {
             synchronized (mLock) {
-                for (Map.Entry<Callback, Executor> e : mCallbacks.entrySet()) {
+                for (Map.Entry<OnMediaKeyEventDispatchedListener, Executor> e
+                        : mOnMediaKeyEventDispatchedListeners.entrySet()) {
                     e.getValue().execute(
-                            () -> e.getKey().onMediaKeyEventDispatched(event, sessionToken));
+                            () -> e.getKey().onMediaKeyEventDispatched(event, packageName,
+                                    sessionToken));
                 }
             }
         }
+    }
 
+    private final class OnMediaKeyEventSessionChangedListenerStub
+            extends IOnMediaKeyEventSessionChangedListener.Stub {
         @Override
-        public void onMediaKeyEventDispatchedToMediaButtonReceiver(KeyEvent event,
-                ComponentName mediaButtonReceiver) {
+        public void onMediaKeyEventSessionChanged(String packageName,
+                MediaSession.Token sessionToken) {
             synchronized (mLock) {
-                for (Map.Entry<Callback, Executor> e : mCallbacks.entrySet()) {
-                    e.getValue().execute(
-                            () -> e.getKey().onMediaKeyEventDispatched(event, mediaButtonReceiver));
-                }
-            }
-        }
-
-        @Override
-        public void onAddressedPlayerChangedToMediaSession(MediaSession.Token sessionToken) {
-            synchronized (mLock) {
-                mCurMediaButtonSession = sessionToken;
-                mCurMediaButtonReceiver = null;
-                for (Map.Entry<Callback, Executor> e : mCallbacks.entrySet()) {
-                    e.getValue().execute(() -> e.getKey().onAddressedPlayerChanged(sessionToken));
-                }
-            }
-        }
-
-        @Override
-        public void onAddressedPlayerChangedToMediaButtonReceiver(
-                ComponentName mediaButtonReceiver) {
-            synchronized (mLock) {
-                mCurMediaButtonSession = null;
-                mCurMediaButtonReceiver = mediaButtonReceiver;
-                for (Map.Entry<Callback, Executor> e : mCallbacks.entrySet()) {
-                    e.getValue().execute(() -> e.getKey().onAddressedPlayerChanged(
-                            mediaButtonReceiver));
+                mCurMediaKeyEventSessionPackage = packageName;
+                mCurMediaKeyEventSession = sessionToken;
+                for (Map.Entry<OnMediaKeyEventSessionChangedListener, Executor> e
+                        : mMediaKeyEventSessionChangedCallbacks.entrySet()) {
+                    e.getValue().execute(() -> e.getKey().onMediaKeyEventSessionChanged(packageName,
+                            sessionToken));
                 }
             }
         }
diff --git a/media/java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl b/media/java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
new file mode 100644
index 0000000..3dbc705
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+/**
+ * A recognition confidence level.
+ * This type is used to represent either a threshold or an actual detection confidence level.
+ *
+ * {@hide}
+ */
+parcelable ConfidenceLevel {
+    /** user ID. */
+    int userId;
+    /**
+     * Confidence level in percent (0 - 100).
+     * <ul>
+     * <li>Min level for recognition configuration
+     * <li>Detected level for recognition event.
+     * </ul>
+     */
+    int levelPercent;
+}
diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl b/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
new file mode 100644
index 0000000..149c1cd
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
@@ -0,0 +1,47 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+
+/**
+ * Main interface for a client to get notifications of events coming from this module.
+ *
+ * {@hide}
+ */
+oneway interface ISoundTriggerCallback {
+    /**
+     * Invoked whenever a recognition event is triggered (typically, on recognition, but also in
+     * case of external aborting of a recognition or a forced recognition event - see the status
+     * code in the event for determining).
+     */
+    void onRecognition(int modelHandle, in RecognitionEvent event);
+     /**
+      * Invoked whenever a phrase recognition event is triggered (typically, on recognition, but
+      * also in case of external aborting of a recognition or a forced recognition event - see the
+      * status code in the event for determining).
+      */
+    void onPhraseRecognition(int modelHandle, in PhraseRecognitionEvent event);
+    /**
+     * Notifies the client the recognition has become available after previously having been
+     * unavailable, or vice versa. This method will always be invoked once immediately after
+     * attachment, and then every time there is a change in availability.
+     * When availability changes from available to unavailable, all active recognitions are aborted,
+     * and this event will be sent in addition to the abort event.
+     */
+    void onRecognitionAvailabilityChange(boolean available);
+}
diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl b/media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl
new file mode 100644
index 0000000..8033307
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl
@@ -0,0 +1,48 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+import android.media.soundtrigger_middleware.ISoundTriggerModule;
+import android.media.soundtrigger_middleware.ISoundTriggerCallback;
+import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+
+/**
+ * Main entry point into this module.
+ *
+ * Allows the client to enumerate the available soundtrigger devices and their capabilities, then
+ * attach to either one of them in order to use it.
+ *
+ * {@hide}
+ */
+interface ISoundTriggerMiddlewareService {
+    /**
+     * Query the available modules and their capabilities.
+     */
+    SoundTriggerModuleDescriptor[] listModules();
+
+    /**
+     * Attach to one of the available modules.
+     * listModules() must be called prior to calling this method and the provided handle must be
+     * one of the handles from the returned list.
+     */
+    ISoundTriggerModule attach(int handle, ISoundTriggerCallback callback);
+
+    /**
+     * Notify the service that external input capture is taking place. This may cause some of the
+     * active recognitions to be aborted.
+     */
+    void setExternalCaptureState(boolean active);
+}
\ No newline at end of file
diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl b/media/java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
new file mode 100644
index 0000000..c4a5785
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
@@ -0,0 +1,148 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+import android.media.soundtrigger_middleware.ModelParameter;
+import android.media.soundtrigger_middleware.ModelParameterRange;
+import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger_middleware.PhraseSoundModel;
+import android.media.soundtrigger_middleware.RecognitionConfig;
+
+/**
+ * A sound-trigger module.
+ *
+ * This interface allows a client to operate a sound-trigger device, intended for low-power
+ * detection of various sound patterns, represented by a "sound model".
+ *
+ * Basic operation is to load a sound model (either a generic one or a "phrase" model), then
+ * initiate recognition on this model. A trigger will be delivered asynchronously via a callback
+ * provided by the caller earlier, when attaching to this interface.
+ *
+ * In additon to recognition events, this module will also produce abort events in cases where
+ * recognition has been externally preempted.
+ *
+ * {@hide}
+ */
+interface ISoundTriggerModule {
+    /**
+     * Load a sound model. Will return a handle to the model on success or will throw a
+     * ServiceSpecificException with one of the {@link Status} error codes upon a recoverable error
+     * (for example, lack of resources of loading a model at the time of call.
+     * Model must eventually be unloaded using {@link #unloadModel(int)} prior to detaching.
+     *
+     * May throw a ServiceSpecificException with an RESOURCE_CONTENTION status to indicate that
+     * resources required for loading the model are currently consumed by other clients.
+     */
+    int loadModel(in SoundModel model);
+
+    /**
+     * Load a phrase sound model. Will return a handle to the model on success or will throw a
+     * ServiceSpecificException with one of the {@link Status} error codes upon a recoverable error
+     * (for example, lack of resources of loading a model at the time of call.
+     * Model must eventually be unloaded using unloadModel prior to detaching.
+     *
+     * May throw a ServiceSpecificException with an RESOURCE_CONTENTION status to indicate that
+     * resources required for loading the model are currently consumed by other clients.
+     */
+    int loadPhraseModel(in PhraseSoundModel model);
+
+    /**
+     * Unload a model, previously loaded with loadModel or loadPhraseModel. After unloading, model
+     * can no longer be used for recognition and the resources occupied by it are released.
+     * Model must not be active at the time of unloading. Cient may call stopRecognition to ensure
+     * that.
+     */
+    void unloadModel(int modelHandle);
+
+    /**
+     * Initiate recognition on a previously loaded model.
+     * Recognition event would eventually be delivered via the client-provided callback, typically
+     * supplied during attachment to this interface.
+     *
+     * Once a recognition event is passed to the client, the recognition automatically become
+     * inactive, unless the event is of the RecognitionStatus.FORCED kind. Client can also shut down
+     * the recognition explicitly, via stopRecognition.
+     */
+    void startRecognition(int modelHandle, in RecognitionConfig config);
+
+    /**
+     * Stop a recognition of a previously active recognition. Will NOT generate a recognition event.
+     * This call is idempotent - calling it on an inactive model has no effect. However, it must
+     * only be used with a loaded model handle.
+     */
+    void stopRecognition(int modelHandle);
+
+    /**
+     * Force generation of a recognition event. Handle must be that of a loaded model. If
+     * recognition is inactive, will do nothing. If recognition is active, will asynchronously
+     * deliever an event with RecognitionStatus.FORCED status and leave recognition in active state.
+     * To avoid any race conditions, once an event signalling the automatic stopping of recognition
+     * is sent, no more forced events will get sent (even if previously requested) until recognition
+     * is explicitly started again.
+     *
+     * Since not all module implementations support this feature, may throw a
+     * ServiceSpecificException with an OPERATION_NOT_SUPPORTED status.
+     */
+    void forceRecognitionEvent(int modelHandle);
+
+    /**
+     * Set a model specific parameter with the given value. This parameter
+     * will keep its value for the duration the model is loaded regardless of starting and stopping
+     * recognition. Once the model is unloaded, the value will be lost.
+     * It is expected to check if the handle supports the parameter via the
+     * queryModelParameterSupport API prior to calling this method.
+     *
+     * @param modelHandle The sound model handle indicating which model to modify parameters
+     * @param modelParam Parameter to set which will be validated against the
+     *                   ModelParameter type.
+     * @param value The value to set for the given model parameter
+     */
+    void setModelParameter(int modelHandle, ModelParameter modelParam, int value);
+
+    /**
+     * Get a model specific parameter. This parameter will keep its value
+     * for the duration the model is loaded regardless of starting and stopping recognition.
+     * Once the model is unloaded, the value will be lost. If the value is not set, a default
+     * value is returned. See ModelParameter for parameter default values.
+     * It is expected to check if the handle supports the parameter via the
+     * queryModelParameterSupport API prior to calling this method.
+     *
+     * @param modelHandle The sound model associated with given modelParam
+     * @param modelParam Parameter to set which will be validated against the
+     *                   ModelParameter type.
+     * @return Value set to the requested parameter.
+     */
+    int getModelParameter(int modelHandle, ModelParameter modelParam);
+
+    /**
+     * Determine if parameter control is supported for the given model handle, and its valid value
+     * range if it is.
+     *
+     * @param modelHandle The sound model handle indicating which model to query
+     * @param modelParam Parameter to set which will be validated against the
+     *                   ModelParameter type.
+     * @return If parameter is supported, the return value is its valid range, otherwise null.
+     */
+    @nullable ModelParameterRange queryModelParameterSupport(int modelHandle,
+                                                             ModelParameter modelParam);
+
+    /**
+     * Detach from the module, releasing any active resources.
+     * This will ensure the client callback is no longer called after this call returns.
+     * All models must have been unloaded prior to calling this method.
+     */
+    void detach();
+}
\ No newline at end of file
diff --git a/media/java/android/media/soundtrigger_middleware/ModelParameter.aidl b/media/java/android/media/soundtrigger_middleware/ModelParameter.aidl
new file mode 100644
index 0000000..0993627
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/ModelParameter.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger_middleware;
+
+/**
+ * Model specific parameters to be used with parameter set and get APIs.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+enum ModelParameter {
+    /**
+     * Placeholder for invalid model parameter used for returning error or
+     * passing an invalid value.
+     */
+    INVALID = -1,
+
+    /**
+     * Controls the sensitivity threshold adjustment factor for a given model.
+     * Negative value corresponds to less sensitive model (high threshold) and
+     * a positive value corresponds to a more sensitive model (low threshold).
+     * Default value is 0.
+     */
+    THRESHOLD_FACTOR = 0,
+}
diff --git a/media/java/android/media/soundtrigger_middleware/ModelParameterRange.aidl b/media/java/android/media/soundtrigger_middleware/ModelParameterRange.aidl
new file mode 100644
index 0000000..d6948a8
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/ModelParameterRange.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+/**
+ * Value range for a model parameter.
+ *
+ * {@hide}
+ */
+parcelable ModelParameterRange {
+    /** Minimum (inclusive) */
+    int minInclusive;
+    /** Maximum (inclusive) */
+    int maxInclusive;
+}
diff --git a/media/java/android/media/soundtrigger_middleware/Phrase.aidl b/media/java/android/media/soundtrigger_middleware/Phrase.aidl
new file mode 100644
index 0000000..98a489f8
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/Phrase.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+/**
+ * Key phrase descriptor.
+ *
+ * {@hide}
+ */
+parcelable Phrase {
+    /** Unique keyphrase ID assigned at enrollment time. */
+    int id;
+    /** Recognition modes supported by this key phrase (bitfield of RecognitionMode enum). */
+    int recognitionModes;
+    /** List of users IDs associated with this key phrase. */
+    int[] users;
+    /** Locale - Java Locale style (e.g. en_US). */
+    String locale;
+    /** Phrase text. */
+    String text;
+}
diff --git a/media/java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl b/media/java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
new file mode 100644
index 0000000..6a3ec61
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
+import android.media.soundtrigger_middleware.RecognitionEvent;
+
+/**
+ * An event that gets sent to indicate a phrase recognition (or aborting of the recognition
+   process).
+ * {@hide}
+ */
+parcelable PhraseRecognitionEvent {
+    /** Common recognition event. */
+    RecognitionEvent common;
+    /** List of descriptors for each recognized key phrase */
+    PhraseRecognitionExtra[] phraseExtras;
+}
diff --git a/media/java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl b/media/java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
new file mode 100644
index 0000000..cb96bf3
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+import android.media.soundtrigger_middleware.ConfidenceLevel;
+
+/**
+ * Specialized recognition event for key phrase detection.
+ * {@hide}
+ */
+parcelable PhraseRecognitionExtra {
+    // TODO(ytai): Constants / enums.
+
+    /** keyphrase ID */
+    int id;
+    /** recognition modes used for this keyphrase */
+    int recognitionModes;
+    /** confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER */
+    int confidenceLevel;
+    /** number of user confidence levels */
+    ConfidenceLevel[] levels;
+}
diff --git a/media/java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl b/media/java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
new file mode 100644
index 0000000..81028c1
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger_middleware;
+
+import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger_middleware.Phrase;
+
+/**
+ * Specialized sound model for key phrase detection.
+ * Proprietary representation of key phrases in binary data must match
+ * information indicated by phrases field.
+ * {@hide}
+ */
+parcelable PhraseSoundModel {
+    /** Common part of sound model descriptor */
+    SoundModel common;
+    /** List of descriptors for key phrases supported by this sound model */
+    Phrase[] phrases;
+}
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl b/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl
new file mode 100644
index 0000000..c7642e8
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
+
+/**
+ * Configuration for tuning behavior of an active recognition process.
+ * {@hide}
+ */
+parcelable RecognitionConfig {
+    /* Capture and buffer audio for this recognition instance. */
+    boolean captureRequested;
+
+    /* Configuration for each key phrase. */
+    PhraseRecognitionExtra[] phraseRecognitionExtras;
+
+    /** Opaque capture configuration data. */
+    byte[] data;
+}
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl b/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl
new file mode 100644
index 0000000..de4d060
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger_middleware;
+
+import android.media.audio.common.AudioConfig;
+import android.media.soundtrigger_middleware.RecognitionStatus;
+import android.media.soundtrigger_middleware.SoundModelType;
+
+/**
+ * An event that gets sent to indicate a recognition (or aborting of the recognition process).
+ * {@hide}
+ */
+parcelable RecognitionEvent {
+    /** Recognition status. */
+    RecognitionStatus status;
+    /** Event type, same as sound model type. */
+    SoundModelType type;
+    /** Is it possible to capture audio from this utterance buffered by the implementation. */
+    boolean captureAvailable;
+    /* Audio session ID. framework use. */
+    int captureSession;
+    /**
+     * Delay in ms between end of model detection and start of audio available for capture.
+     * A negative value is possible (e.g. if key phrase is also available for Capture.
+     */
+    int captureDelayMs;
+    /** Duration in ms of audio captured before the start of the trigger. 0 if none. */
+    int capturePreambleMs;
+    /** If true, the 'data' field below contains the capture of the trigger sound. */
+    boolean triggerInData;
+    /**
+     * Audio format of either the trigger in event data or to use for capture of the rest of the
+     * utterance.
+     */
+    AudioConfig audioConfig;
+    /** Additional data. */
+    byte[] data;
+}
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionMode.aidl b/media/java/android/media/soundtrigger_middleware/RecognitionMode.aidl
new file mode 100644
index 0000000..d8bfff4
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/RecognitionMode.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger_middleware;
+
+/**
+ * Recognition mode.
+ * {@hide}
+ */
+@Backing(type="int")
+enum RecognitionMode {
+    /** Simple voice trigger. */
+    VOICE_TRIGGER       = 0x1,
+    /** Trigger only if one user in model identified. */
+    USER_IDENTIFICATION = 0x2,
+    /** Trigger only if one user in model authenticated. */
+    USER_AUTHENTICATION = 0x4,
+    /** Generic sound trigger. */
+    GENERIC_TRIGGER     = 0x8,
+}
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionStatus.aidl b/media/java/android/media/soundtrigger_middleware/RecognitionStatus.aidl
new file mode 100644
index 0000000..d563edc
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/RecognitionStatus.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+/**
+ * A status for indicating the type of a recognition event.
+ * {@hide}
+ */
+@Backing(type="int")
+enum RecognitionStatus {
+    /** Recognition success. */
+    SUCCESS = 0,
+    /** Recognition aborted (e.g. capture preempted by another use-case. */
+    ABORTED = 1,
+    /** Recognition failure. */
+    FAILURE = 2,
+    /**
+    * Recognition event was triggered by a forceRecognitionEvent request, not by the DSP.
+    * Note that forced detections *do not* stop the active recognition, unlike the other types.
+    */
+    FORCED = 3
+}
diff --git a/media/java/android/media/soundtrigger_middleware/SoundModel.aidl b/media/java/android/media/soundtrigger_middleware/SoundModel.aidl
new file mode 100644
index 0000000..fba1ee50
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/SoundModel.aidl
@@ -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.media.soundtrigger_middleware;
+
+import android.media.soundtrigger_middleware.SoundModelType;
+
+/**
+ * Base sound model descriptor. This struct can be extended for various specific types by way of
+ * aggregation.
+ * {@hide}
+ */
+parcelable SoundModel {
+    /** Model type. */
+    SoundModelType type;
+    /** Unique sound model ID. */
+    String uuid;
+    /**
+     * Unique vendor ID. Identifies the engine the sound model
+     * was build for */
+    String vendorUuid;
+    /** Opaque data transparent to Android framework */
+    byte[] data;
+}
diff --git a/media/java/android/media/soundtrigger_middleware/SoundModelType.aidl b/media/java/android/media/soundtrigger_middleware/SoundModelType.aidl
new file mode 100644
index 0000000..f2abc9a
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/SoundModelType.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.soundtrigger_middleware;
+
+/**
+ * Sound model type.
+ * {@hide}
+ */
+@Backing(type="int")
+enum SoundModelType {
+    /** Unspecified sound model type */
+    UNKNOWN = -1,
+    /** Key phrase sound models */
+    KEYPHRASE = 0,
+    /** All models other than keyphrase */
+    GENERIC = 1,
+}
diff --git a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl b/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
new file mode 100644
index 0000000..667135f
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
+
+/**
+ * A descriptor of an available sound trigger module, containing the handle used to reference the
+ * module, as well its capabilities.
+ * {@hide}
+ */
+parcelable SoundTriggerModuleDescriptor {
+    /** Module handle to be used for attaching to it. */
+    int handle;
+    /** Module capabilities. */
+    SoundTriggerModuleProperties properties;
+}
+
diff --git a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl b/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
new file mode 100644
index 0000000..1a3b402
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+/**
+ * Capabilities of a sound trigger module.
+ * {@hide}
+ */
+parcelable SoundTriggerModuleProperties {
+    /** Implementor name */
+    String   implementor;
+    /** Implementation description */
+    String   description;
+    /** Implementation version */
+    int version;
+    /**
+     * Unique implementation ID. The UUID must change with each version of
+       the engine implementation */
+    String     uuid;
+    /** Maximum number of concurrent sound models loaded */
+    int maxSoundModels;
+    /** Maximum number of key phrases */
+    int maxKeyPhrases;
+    /** Maximum number of concurrent users detected */
+    int maxUsers;
+    /** All supported modes. e.g RecognitionMode.VOICE_TRIGGER */
+    int recognitionModes;
+    /** Supports seamless transition from detection to capture */
+    boolean     captureTransition;
+    /** Maximum buffering capacity in ms if captureTransition is true */
+    int maxBufferMs;
+    /** Supports capture by other use cases while detection is active */
+    boolean     concurrentCapture;
+    /** Returns the trigger capture in event */
+    boolean     triggerInEvent;
+    /**
+     * Rated power consumption when detection is active with TDB
+     * silence/sound/speech ratio */
+    int powerConsumptionMw;
+}
diff --git a/media/java/android/media/soundtrigger_middleware/Status.aidl b/media/java/android/media/soundtrigger_middleware/Status.aidl
new file mode 100644
index 0000000..d8f9d8f
--- /dev/null
+++ b/media/java/android/media/soundtrigger_middleware/Status.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.media.soundtrigger_middleware;
+
+/**
+ * {@hide}
+ */
+@Backing(type="int")
+enum Status {
+    /** Success. */
+    SUCCESS = 0,
+    /** Failure due to resource contention. This is typically a temporary condition. */
+    RESOURCE_CONTENTION = 1,
+    /** Operation is not supported in this implementation. This is a permanent condition. */
+    OPERATION_NOT_SUPPORTED = 2,
+}
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 16ba63b..0f402eb 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -577,7 +577,7 @@
         try {
             // note - we are relying on a special case in MediaProvider.update() to update
             // the paths for all children in the case where this is a directory.
-            final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName());
+            final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
             mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in mMediaProvider.update", e);
@@ -658,7 +658,7 @@
                 // Old parent exists in MediaProvider - perform a move
                 // note - we are relying on a special case in MediaProvider.update() to update
                 // the paths for all children in the case where this is a directory.
-                final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName());
+                final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
                 mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs);
             } else {
                 // Old parent doesn't exist - add the object
@@ -873,7 +873,7 @@
     }
 
     private int findInMedia(MtpStorageManager.MtpObject obj, Path path) {
-        final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName());
+        final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
 
         int ret = -1;
         Cursor c = null;
@@ -893,7 +893,7 @@
     }
 
     private void deleteFromMedia(MtpStorageManager.MtpObject obj, Path path, boolean isDir) {
-        final Uri objectsUri = MediaStore.Files.getMtpObjectsUri(obj.getVolumeName());
+        final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName());
         try {
             // Delete the object(s) from MediaProvider, but ignore errors.
             if (isDir) {
@@ -921,71 +921,12 @@
 
     @VisibleForNative
     private int[] getObjectReferences(int handle) {
-        MtpStorageManager.MtpObject obj = mManager.getObject(handle);
-        if (obj == null)
-            return null;
-        // Translate this handle to the MediaProvider Handle
-        handle = findInMedia(obj, obj.getPath());
-        if (handle == -1)
-            return null;
-        Uri uri = Files.getMtpReferencesUri(obj.getVolumeName(), handle);
-        Cursor c = null;
-        try {
-            c = mMediaProvider.query(uri, PATH_PROJECTION, null, null, null, null);
-            if (c == null) {
-                return null;
-            }
-                ArrayList<Integer> result = new ArrayList<>();
-                while (c.moveToNext()) {
-                    // Translate result handles back into handles for this session.
-                    String refPath = c.getString(0);
-                    MtpStorageManager.MtpObject refObj = mManager.getByPath(refPath);
-                    if (refObj != null) {
-                        result.add(refObj.getId());
-                    }
-                }
-                return result.stream().mapToInt(Integer::intValue).toArray();
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in getObjectList", e);
-        } finally {
-            if (c != null) {
-                c.close();
-            }
-        }
         return null;
     }
 
     @VisibleForNative
     private int setObjectReferences(int handle, int[] references) {
-        MtpStorageManager.MtpObject obj = mManager.getObject(handle);
-        if (obj == null)
-            return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
-        // Translate this handle to the MediaProvider Handle
-        handle = findInMedia(obj, obj.getPath());
-        if (handle == -1)
-            return MtpConstants.RESPONSE_GENERAL_ERROR;
-        Uri uri = Files.getMtpReferencesUri(obj.getVolumeName(), handle);
-        ArrayList<ContentValues> valuesList = new ArrayList<>();
-        for (int id : references) {
-            // Translate each reference id to the MediaProvider Id
-            MtpStorageManager.MtpObject refObj = mManager.getObject(id);
-            if (refObj == null)
-                continue;
-            int refHandle = findInMedia(refObj, refObj.getPath());
-            if (refHandle == -1)
-                continue;
-            ContentValues values = new ContentValues();
-            values.put(Files.FileColumns._ID, refHandle);
-            valuesList.add(values);
-        }
-        try {
-            if (mMediaProvider.bulkInsert(uri, valuesList.toArray(new ContentValues[0])) > 0) {
-                return MtpConstants.RESPONSE_OK;
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in setObjectReferences", e);
-        }
-        return MtpConstants.RESPONSE_GENERAL_ERROR;
+        return MtpConstants.RESPONSE_OPERATION_NOT_SUPPORTED;
     }
 
     @VisibleForNative
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 05aaa82..20980a9 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -742,11 +742,11 @@
     return OK;
 }
 
-status_t JMediaCodec::getMetrics(JNIEnv *, MediaAnalyticsItem * &reply) const {
-    mediametrics_handle_t reply2 = MediaAnalyticsItem::convert(reply);
+status_t JMediaCodec::getMetrics(JNIEnv *, mediametrics::Item * &reply) const {
+    mediametrics_handle_t reply2 = mediametrics::Item::convert(reply);
     status_t status = mCodec->getMetrics(reply2);
     // getMetrics() updates reply2, pass the converted update along to our caller.
-    reply = MediaAnalyticsItem::convert(reply2);
+    reply = mediametrics::Item::convert(reply2);
     return status;
 }
 
@@ -1850,7 +1850,7 @@
     }
 
     // get what we have for the metrics from the codec
-    MediaAnalyticsItem *item = 0;
+    mediametrics::Item *item = 0;
 
     status_t err = codec->getMetrics(env, item);
     if (err != OK) {
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index dfe30a3..ce1c805 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -21,7 +21,7 @@
 
 #include "jni.h"
 
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
 #include <media/hardware/CryptoAPI.h>
 #include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/foundation/AHandler.h>
@@ -124,7 +124,7 @@
 
     status_t getCodecInfo(JNIEnv *env, jobject *codecInfo) const;
 
-    status_t getMetrics(JNIEnv *env, MediaAnalyticsItem * &reply) const;
+    status_t getMetrics(JNIEnv *env, mediametrics::Item * &reply) const;
 
     status_t setParameters(const sp<AMessage> &params);
 
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index f5ae9d0..528dc62 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -913,7 +913,7 @@
     }
 
     // build and return the Bundle
-    std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create());
+    std::unique_ptr<mediametrics::Item> item(mediametrics::Item::create());
     item->readFromParcel(reply);
     jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item.get(), NULL);
 
diff --git a/media/jni/android_media_MediaMetricsJNI.cpp b/media/jni/android_media_MediaMetricsJNI.cpp
index d315154..494c617 100644
--- a/media/jni/android_media_MediaMetricsJNI.cpp
+++ b/media/jni/android_media_MediaMetricsJNI.cpp
@@ -18,7 +18,7 @@
 
 #include <binder/Parcel.h>
 #include <jni.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
 #include <nativehelper/JNIHelp.h>
 
 #include "android_media_MediaMetricsJNI.h"
@@ -52,7 +52,7 @@
     const jmethodID constructID;
     jobject const bundle;
 
-    // We use templated put to access MediaAnalyticsItem based on data type not type enum.
+    // We use templated put to access mediametrics::Item based on data type not type enum.
     // See std::variant and std::visit.
     template<typename T>
     void put(jstring keyName, const T& value) = delete;
@@ -97,7 +97,7 @@
 
 // place the attributes into a java PersistableBundle object
 jobject MediaMetricsJNI::writeMetricsToBundle(
-        JNIEnv* env, MediaAnalyticsItem *item, jobject bundle)
+        JNIEnv* env, mediametrics::Item *item, jobject bundle)
 {
     BundleHelper bh(env, bundle);
 
diff --git a/media/jni/android_media_MediaMetricsJNI.h b/media/jni/android_media_MediaMetricsJNI.h
index 63ec27a..bcad558 100644
--- a/media/jni/android_media_MediaMetricsJNI.h
+++ b/media/jni/android_media_MediaMetricsJNI.h
@@ -19,7 +19,7 @@
 
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
 #include <binder/PersistableBundle.h>
 
 // Copeid from core/jni/ (libandroid_runtime.so)
@@ -27,7 +27,7 @@
 
 class MediaMetricsJNI {
 public:
-    static jobject writeMetricsToBundle(JNIEnv* env, MediaAnalyticsItem *item, jobject mybundle);
+    static jobject writeMetricsToBundle(JNIEnv* env, mediametrics::Item *item, jobject mybundle);
     static jobject nativeToJavaPersistableBundle(JNIEnv*, os::PersistableBundle*);
 };
 
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index b4fa07b..963b650 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -23,7 +23,7 @@
 #include <media/AudioResamplerPublic.h>
 #include <media/IMediaHTTPService.h>
 #include <media/MediaPlayerInterface.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
 #include <media/stagefright/foundation/ByteUtils.h>  // for FOURCC definition
 #include <stdio.h>
 #include <assert.h>
@@ -682,7 +682,7 @@
         return (jobject) NULL;
     }
 
-    std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create());
+    std::unique_ptr<mediametrics::Item> item(mediametrics::Item::create());
     item->readFromParcel(p);
     jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item.get(), NULL);
 
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index f8ba36d..6eeccf0 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -29,7 +29,7 @@
 #include <gui/Surface.h>
 #include <camera/Camera.h>
 #include <media/mediarecorder.h>
-#include <media/MediaAnalyticsItem.h>
+#include <media/MediaMetricsItem.h>
 #include <media/MicrophoneInfo.h>
 #include <media/stagefright/PersistentSurface.h>
 #include <utils/threads.h>
@@ -694,7 +694,7 @@
     }
 
     // build and return the Bundle
-    std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create());
+    std::unique_ptr<mediametrics::Item> item(mediametrics::Item::create());
     item->readFromParcel(reply);
     jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item.get(), NULL);
 
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index 3258d57..2697a10 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -29,6 +29,7 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
+
 import com.android.internal.telephony.PhoneConstants;
 
 /**
@@ -138,7 +139,7 @@
                 SubscriptionManager.getDefaultVoiceSubscriptionId());
         logd("onRegisterDefaultNetworkAvail subId: " + subId);
         final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
-        telephonyMgr.carrierActionReportDefaultNetworkStatus(subId, true);
+        telephonyMgr.createForSubscriptionId(subId).reportDefaultNetworkStatus(true);
     }
 
     private static void onDeregisterDefaultNetworkAvail(Intent intent, Context context) {
@@ -146,7 +147,7 @@
                 SubscriptionManager.getDefaultVoiceSubscriptionId());
         logd("onDeregisterDefaultNetworkAvail subId: " + subId);
         final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
-        telephonyMgr.carrierActionReportDefaultNetworkStatus(subId, false);
+        telephonyMgr.createForSubscriptionId(subId).reportDefaultNetworkStatus(false);
     }
 
     private static void onDisableRadio(Intent intent, Context context) {
@@ -154,7 +155,7 @@
                 SubscriptionManager.getDefaultVoiceSubscriptionId());
         logd("onDisableRadio subId: " + subId);
         final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
-        telephonyMgr.carrierActionSetRadioEnabled(subId, !ENABLE);
+        telephonyMgr.createForSubscriptionId(subId).setRadioEnabled(!ENABLE);
     }
 
     private static void onEnableRadio(Intent intent, Context context) {
@@ -162,7 +163,7 @@
                 SubscriptionManager.getDefaultVoiceSubscriptionId());
         logd("onEnableRadio subId: " + subId);
         final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
-        telephonyMgr.carrierActionSetRadioEnabled(subId, ENABLE);
+        telephonyMgr.createForSubscriptionId(subId).setRadioEnabled(ENABLE);
     }
 
     private static void onShowCaptivePortalNotification(Intent intent, Context context) {
@@ -205,7 +206,7 @@
                 SubscriptionManager.getDefaultVoiceSubscriptionId());
         logd("onResetAllCarrierActions subId: " + subId);
         final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
-        telephonyMgr.carrierActionResetAll(subId);
+        telephonyMgr.createForSubscriptionId(subId).resetAllCarrierActions();
     }
 
     private static Notification getNotification(Context context, int titleId, int textId,
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index ade292f..f40105d 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -598,6 +598,8 @@
     <string name="bluetooth_show_devices_without_names">Show Bluetooth devices without names</string>
     <!-- Setting Checkbox title for disabling Bluetooth absolute volume -->
     <string name="bluetooth_disable_absolute_volume">Disable absolute volume</string>
+    <!-- Setting Checkbox title for enabling Bluetooth Gabeldorsche. [CHAR LIMIT=40] -->
+    <string name="bluetooth_enable_gabeldorsche">Enable Gabeldorsche</string>
 
     <!-- UI debug setting: Select Bluetooth AVRCP Version -->
     <string name="bluetooth_select_avrcp_version_string">Bluetooth AVRCP Version</string>
@@ -696,6 +698,8 @@
     <string name="bluetooth_show_devices_without_names_summary">Bluetooth devices without names (MAC addresses only) will be displayed</string>
     <!-- Summary of checkbox for disabling Bluetooth absolute volume -->
     <string name="bluetooth_disable_absolute_volume_summary">Disables the Bluetooth absolute volume feature in case of volume issues with remote devices such as unacceptably loud volume or lack of control.</string>
+    <!-- Summary of checkbox for enabling Bluetooth Gabeldorche features [CHAR LIMIT=none] -->
+    <string name="bluetooth_enable_gabeldorsche_summary">Enables the Bluetooth Gabeldorche feature stack.</string>
 
     <!-- Title of checkbox setting that enables the terminal app. [CHAR LIMIT=32] -->
     <string name="enable_terminal_title">Local terminal</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index 9c896c8..bc03c34 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -164,13 +164,13 @@
         }
     }
 
-    boolean isA2dpPlaying() {
+    boolean isAudioPlaying() {
         if (mService == null) {
             return false;
         }
         List<BluetoothDevice> srcs = mService.getConnectedDevices();
         if (!srcs.isEmpty()) {
-            if (mService.isA2dpPlaying(srcs.get(0))) {
+            if (mService.isAudioPlaying(srcs.get(0))) {
                 return true;
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index 8f40ab4..80b03a4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -175,7 +175,7 @@
                     return;
                 }
                 A2dpSinkProfile a2dpSink = mProfileManager.getA2dpSinkProfile();
-                if ((a2dpSink != null) && (a2dpSink.isA2dpPlaying())){
+                if ((a2dpSink != null) && (a2dpSink.isAudioPlaying())) {
                     return;
                 }
             }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index d91226e..3f920a8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.bluetooth;
 
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothPbap;
@@ -52,14 +53,16 @@
 
     // These callbacks run on the main thread.
     private final class PbapServiceListener
-            implements BluetoothPbap.ServiceListener {
+            implements BluetoothProfile.ServiceListener {
 
-        public void onServiceConnected(BluetoothPbap proxy) {
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
             mService = (BluetoothPbap) proxy;
             mIsProfileReady=true;
         }
 
-        public void onServiceDisconnected() {
+        @Override
+        public void onServiceDisconnected(int profile) {
             mIsProfileReady=false;
         }
     }
@@ -74,7 +77,8 @@
     }
 
     PbapServerProfile(Context context) {
-        BluetoothPbap pbap = new BluetoothPbap(context, new PbapServiceListener());
+        BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new PbapServiceListener(),
+                BluetoothProfile.PBAP);
     }
 
     public boolean accessProfileEnabled() {
@@ -97,13 +101,8 @@
     }
 
     public int getConnectionStatus(BluetoothDevice device) {
-        if (mService == null) {
-            return BluetoothProfile.STATE_DISCONNECTED;
-        }
-        if (mService.isConnected(device))
-            return BluetoothProfile.STATE_CONNECTED;
-        else
-            return BluetoothProfile.STATE_DISCONNECTED;
+        if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
+        return mService.getConnectionState(device);
     }
 
     public boolean isPreferred(BluetoothDevice device) {
@@ -142,7 +141,8 @@
         Log.d(TAG, "finalize()");
         if (mService != null) {
             try {
-                mService.close();
+                BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.PBAP,
+                        mService);
                 mService = null;
             }catch (Throwable t) {
                 Log.w(TAG, "Error cleaning up PBAP proxy", t);
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
index 104cc8f..d3315ef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
@@ -235,7 +235,7 @@
         public final CharSequence contentDescription;
         public final long requestFinishTime;
 
-        private Request(String packageName, UserHandle userHandle, Drawable icon,
+        public Request(String packageName, UserHandle userHandle, Drawable icon,
                 CharSequence label, boolean isHighBattery, CharSequence contentDescription,
                 long requestFinishTime) {
             this.packageName = packageName;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index 22f47f1..440315f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -152,6 +152,14 @@
         // TODO(b/70983952): Fill this method in
     }
 
+    /**
+     * Result of the sign-in request indecated by the WifiEntry.SIGNIN_STATUS constants
+     */
+    public void onSignInResult(int status) {
+        // TODO(b/70983952): Fill this method in
+    }
+
+
     private void updateIcon(int level) {
         if (level == -1) {
             setIcon(null);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 76e7ab4..22d843b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -142,6 +142,8 @@
         Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST,
         Settings.Secure.SKIP_DIRECTION,
         Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
+        Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT,
+        Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT,
         Settings.Secure.NAVIGATION_MODE,
         Settings.Secure.SKIP_GESTURE_COUNT,
         Settings.Secure.SKIP_TOUCH_COUNT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index ae07598..4b10557 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -216,6 +216,10 @@
         VALIDATORS.put(Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, JSON_OBJECT_VALIDATOR);
         VALIDATORS.put(
                 Secure.NAVIGATION_MODE, new DiscreteValueValidator(new String[] {"0", "1", "2"}));
+        VALIDATORS.put(Secure.BACK_GESTURE_INSET_SCALE_LEFT,
+                new InclusiveFloatRangeValidator(0.0f, Float.MAX_VALUE));
+        VALIDATORS.put(Secure.BACK_GESTURE_INSET_SCALE_RIGHT,
+                new InclusiveFloatRangeValidator(0.0f, Float.MAX_VALUE));
         VALIDATORS.put(Secure.AWARE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.SKIP_GESTURE_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.SKIP_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 80077c8..016896f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2191,6 +2191,14 @@
                 Settings.Secure.NAVIGATION_MODE,
                 SecureSettingsProto.NAVIGATION_MODE);
 
+        dumpSetting(s, p,
+                Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT,
+                SecureSettingsProto.GestureNavigation.BACK_GESTURE_INSET_SCALE_LEFT);
+
+        dumpSetting(s, p,
+                Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT,
+                SecureSettingsProto.GestureNavigation.BACK_GESTURE_INSET_SCALE_RIGHT);
+
         final long nfcPaymentToken = p.start(SecureSettingsProto.NFC_PAYMENT);
         dumpSetting(s, p,
                 Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 4a10e85..5d0db01 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -734,7 +734,8 @@
                  Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
                  Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
                  Settings.Secure.FACE_UNLOCK_RE_ENROLL,
-                 Settings.Secure.TAP_GESTURE);
+                 Settings.Secure.TAP_GESTURE,
+                 Settings.Secure.WINDOW_MAGNIFICATION);
 
     @Test
     public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3514704..a8318d6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -28,11 +28,6 @@
         android:glEsVersion="0x00020000"
         android:required="true" />
 
-    <!-- SysUI must be the one to define this permission; its name is
-         referenced by the core OS. -->
-    <permission android:name="android.permission.systemui.IDENTITY"
-        android:protectionLevel="signature" />
-
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 
     <!-- Used to read wallpaper -->
diff --git a/packages/SystemUI/res/drawable/ic_control_magnification_grey.xml b/packages/SystemUI/res/drawable/ic_control_magnification_grey.xml
new file mode 100644
index 0000000..80ce8c1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_control_magnification_grey.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2019 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<vector android:height="96dp" android:viewportHeight="24"
+    android:viewportWidth="24" android:width="96dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#757575" android:pathData="M15.54,5.54L13.77,7.3 12,5.54 10.23,7.3 8.46,5.54 12,2zM18.46,15.54l-1.76,-1.77L18.46,12l-1.76,-1.77 1.76,-1.77L22,12zM8.46,18.46l1.77,-1.76L12,18.46l1.77,-1.76 1.77,1.76L12,22zM5.54,8.46l1.76,1.77L5.54,12l1.76,1.77 -1.76,1.77L2,12z"/>
+    <path android:fillColor="#757575" android:pathData="M12,12m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/magnifier_controllers.xml b/packages/SystemUI/res/layout/magnifier_controllers.xml
new file mode 100644
index 0000000..0203cd4
--- /dev/null
+++ b/packages/SystemUI/res/layout/magnifier_controllers.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/magnification_controls_size"
+    android:layout_alignParentBottom="true"
+    android:layout_alignParentEnd="true"
+    android:layout_height="@dimen/magnification_controls_size"
+    android:gravity="center">
+
+  <RelativeLayout
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content">
+
+    <ImageView
+        android:focusable="true"
+        android:id="@+id/controller"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_control_magnification_grey" />
+
+    <View
+        android:id="@+id/left_control"
+        android:layout_width="@dimen/magnifier_left_right_controls_width"
+        android:layout_height="@dimen/magnifier_left_right_controls_height"
+        android:layout_alignLeft="@+id/controller"
+        android:layout_centerVertical="true" />
+
+    <View
+        android:id="@+id/up_control"
+        android:layout_width="@dimen/magnifier_up_down_controls_width"
+        android:layout_height="@dimen/magnifier_up_down_controls_height"
+        android:layout_alignTop="@+id/controller"
+        android:layout_centerHorizontal="true" />
+
+    <View
+        android:id="@+id/right_control"
+        android:layout_width="@dimen/magnifier_left_right_controls_width"
+        android:layout_height="@dimen/magnifier_left_right_controls_height"
+        android:layout_alignRight="@+id/controller"
+        android:layout_centerVertical="true" />
+
+    <View
+        android:id="@+id/down_control"
+        android:layout_width="@dimen/magnifier_up_down_controls_width"
+        android:layout_height="@dimen/magnifier_up_down_controls_height"
+        android:layout_alignBottom="@+id/controller"
+        android:layout_centerHorizontal="true" />
+  </RelativeLayout>
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/window_magnifier_view.xml b/packages/SystemUI/res/layout/window_magnifier_view.xml
new file mode 100644
index 0000000..f818612
--- /dev/null
+++ b/packages/SystemUI/res/layout/window_magnifier_view.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+    <SurfaceView
+        android:layout_marginStart="@dimen/magnification_border_size"
+        android:layout_marginTop="@dimen/magnification_border_size"
+        android:id="@+id/surface_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <View
+            android:id="@+id/left_handle"
+            android:layout_width="@dimen/magnification_border_size"
+            android:layout_height="match_parent"
+            android:layout_above="@+id/drag_handle"
+            android:background="@color/magnification_border_color" />
+
+        <View
+            android:id="@+id/top_handle"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/magnification_border_size"
+            android:background="@color/magnification_border_color" />
+
+        <View
+            android:id="@+id/right_handle"
+            android:layout_width="@dimen/magnification_border_size"
+            android:layout_height="match_parent"
+            android:layout_above="@+id/drag_handle"
+            android:layout_alignParentEnd="true"
+            android:background="@color/magnification_border_color" />
+
+        <View
+            android:id="@+id/bottom_handle"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/magnification_border_size"
+            android:layout_above="@+id/drag_handle"
+            android:background="@color/magnification_border_color" />
+
+        <View
+            android:id="@+id/drag_handle"
+            android:layout_width="@dimen/magnification_drag_view_width"
+            android:layout_height="@dimen/magnification_drag_view_height"
+            android:layout_alignParentBottom="true"
+            android:layout_centerHorizontal="true"
+            android:background="@color/magnification_border_color" />
+
+    </RelativeLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 92c7477..c142465 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -211,4 +211,5 @@
     <color name="GM2_green_500">#FF34A853</color>
     <color name="GM2_blue_500">#FF4285F4</color>
 
+    <color name="magnification_border_color">#FF9900</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index e896c16..640f31b 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -295,6 +295,7 @@
         <item>com.android.systemui.SizeCompatModeActivityController</item>
         <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
         <item>com.android.systemui.theme.ThemeOverlayController</item>
+        <item>com.android.systemui.accessibility.WindowMagnification</item>
     </string-array>
 
     <!-- SystemUI vender service, used in config_systemUIServiceComponents. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c948116..da0323a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1139,4 +1139,16 @@
     <dimen name="qs_media_width">350dp</dimen>
     <dimen name="qs_media_padding">8dp</dimen>
     <dimen name="qs_media_corner_radius">10dp</dimen>
+
+    <dimen name="magnification_border_size">5dp</dimen>
+    <dimen name="magnification_frame_move_short">5dp</dimen>
+    <dimen name="magnification_frame_move_long">25dp</dimen>
+    <dimen name="magnification_drag_view_width">100dp</dimen>
+    <dimen name="magnification_drag_view_height">35dp</dimen>
+    <dimen name="magnification_controls_size">90dp</dimen>
+    <dimen name="magnifier_left_right_controls_width">35dp</dimen>
+    <dimen name="magnifier_left_right_controls_height">45dp</dimen>
+    <dimen name="magnifier_up_down_controls_width">45dp</dimen>
+    <dimen name="magnifier_up_down_controls_height">40dp</dimen>
+
 </resources>
diff --git a/packages/SystemUI/res/values/integers.xml b/packages/SystemUI/res/values/integers.xml
index deae7e2..c1cf7b4 100644
--- a/packages/SystemUI/res/values/integers.xml
+++ b/packages/SystemUI/res/values/integers.xml
@@ -32,4 +32,6 @@
     <!-- Ratio of "right" end of status bar that will swipe to QS. -->
     <integer name="qs_split_fraction">2</integer>
 
+    <integer name="magnification_default_scale">2</integer>
+
 </resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1053750..bc808b3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2492,4 +2492,12 @@
 
     <!-- Title of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=25] -->
     <string name="inattentive_sleep_warning_title">Standby</string>
+
+    <!-- Window Magnification strings -->
+    <!-- Title for Magnification Overlay Window [CHAR LIMIT=NONE] -->
+    <string name="magnification_overlay_title">Magnification Overlay Window</string>
+    <!-- Title for Magnification Window [CHAR LIMIT=NONE] -->
+    <string name="magnification_window_title">Magnification Window</string>
+    <!-- Title for Magnification Controls Window [CHAR LIMIT=NONE] -->
+    <string name="magnification_controls_title">Magnification Window Controls</string>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 22d1675c..9f13718 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -27,6 +27,7 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -215,4 +216,23 @@
     public void removePinnedStackListener(PinnedStackListener listener) {
         mPinnedStackListenerForwarder.removeListener(listener);
     }
+
+    /**
+     * Mirrors a specified display. The SurfaceControl returned is the root of the mirrored
+     * hierarchy.
+     *
+     * @param displayId The id of the display to mirror
+     * @return The SurfaceControl for the root of the mirrored hierarchy.
+     */
+    public SurfaceControl mirrorDisplay(final int displayId) {
+        try {
+            SurfaceControl outSurfaceControl = new SurfaceControl();
+            WindowManagerGlobal.getWindowManagerService().mirrorDisplay(displayId,
+                    outSurfaceControl);
+            return outSurfaceControl;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to reach window manager", e);
+        }
+        return null;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index 5d084e7..c2cedad 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -30,6 +30,7 @@
 import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.Slog;
 import android.view.MotionEvent;
 import android.view.View;
@@ -114,11 +115,7 @@
         super.onFinishInflate();
         mLockPatternUtils = new LockPatternUtils(mContext);
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        setOnClickListener(new OnClickListener() {
-            public void onClick(View v) {
-                takeEmergencyCallAction();
-            }
-        });
+        setOnClickListener(v -> takeEmergencyCallAction());
         setOnLongClickListener(new OnLongClickListener() {
             @Override
             public boolean onLongClick(View v) {
@@ -168,9 +165,9 @@
      */
     public void takeEmergencyCallAction() {
         MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_CALL);
-        // TODO: implement a shorter timeout once new PowerManager API is ready.
-        // should be the equivalent to the old userActivity(EMERGENCY_CALL_TIMEOUT)
-        mPowerManager.userActivity(SystemClock.uptimeMillis(), true);
+        if (mPowerManager != null) {
+            mPowerManager.userActivity(SystemClock.uptimeMillis(), true);
+        }
         try {
             ActivityTaskManager.getService().stopSystemLockTaskMode();
         } catch (RemoteException e) {
@@ -182,10 +179,19 @@
                 mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall();
             }
         } else {
-            Dependency.get(KeyguardUpdateMonitor.class).reportEmergencyCallAction(
-                    true /* bypassHandler */);
+            KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
+            if (updateMonitor != null) {
+                updateMonitor.reportEmergencyCallAction(true /* bypassHandler */);
+            } else {
+                Log.w(LOG_TAG, "KeyguardUpdateMonitor was null, launching intent anyway.");
+            }
+            TelecomManager telecomManager = getTelecommManager();
+            if (telecomManager == null) {
+                Log.wtf(LOG_TAG, "TelecomManager was null, cannot launch emergency dialer");
+                return;
+            }
             Intent emergencyDialIntent =
-                    getTelecommManager().createLaunchEmergencyDialerIntent(null /* number*/)
+                    telecomManager.createLaunchEmergencyDialerIntent(null /* number*/)
                             .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                                     | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
                                     | Intent.FLAG_ACTIVITY_CLEAR_TOP)
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
new file mode 100644
index 0000000..6178ff2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -0,0 +1,83 @@
+/*
+ * 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.accessibility;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.dagger.qualifiers.MainHandler;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Class to handle changes to setting window_magnification value.
+ */
+@Singleton
+public class WindowMagnification extends SystemUI {
+    private WindowMagnificationController mWindowMagnificationController;
+    private final Handler mHandler;
+
+    @Inject
+    public WindowMagnification(Context context, @MainHandler Handler mainHandler) {
+        super(context);
+        mHandler = mainHandler;
+    }
+
+    @Override
+    public void start() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.WINDOW_MAGNIFICATION),
+                true, new ContentObserver(mHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        updateWindowMagnification();
+                    }
+                });
+    }
+
+    private void updateWindowMagnification() {
+        try {
+            boolean enable = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.WINDOW_MAGNIFICATION) != 0;
+            if (enable) {
+                enableMagnification();
+            } else {
+                disableMagnification();
+            }
+        } catch (Settings.SettingNotFoundException e) {
+            disableMagnification();
+        }
+    }
+
+    private void enableMagnification() {
+        if (mWindowMagnificationController == null) {
+            mWindowMagnificationController = new WindowMagnificationController(mContext, mHandler);
+        }
+        mWindowMagnificationController.createWindowMagnification();
+    }
+
+    private void disableMagnification() {
+        if (mWindowMagnificationController != null) {
+            mWindowMagnificationController.deleteWindowMagnification();
+        }
+        mWindowMagnificationController = null;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
new file mode 100644
index 0000000..e3694ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -0,0 +1,424 @@
+/*
+ * 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.accessibility;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.Handler;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+/**
+ * Class to handle adding and removing a window magnification.
+ */
+public class WindowMagnificationController implements View.OnClickListener,
+        View.OnLongClickListener, View.OnTouchListener, SurfaceHolder.Callback {
+    private final int mBorderSize;
+    private final int mMoveFrameAmountShort;
+    private final int mMoveFrameAmountLong;
+
+    private final Context mContext;
+    private final Point mDisplaySize = new Point();
+    private final int mDisplayId;
+    private final Handler mHandler;
+    private final Rect mMagnificationFrame = new Rect();
+    private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+
+    private final WindowManager mWm;
+
+    private float mScale;
+
+    private final Rect mTmpRect = new Rect();
+
+    // The root of the mirrored content
+    private SurfaceControl mMirrorSurface;
+
+    private boolean mIsPressedDown;
+
+    private View mLeftControl;
+    private View mUpControl;
+    private View mRightControl;
+    private View mBottomControl;
+
+    private View mDragView;
+    private View mLeftDrag;
+    private View mTopDrag;
+    private View mRightDrag;
+    private View mBottomDrag;
+
+    private final PointF mLastDrag = new PointF();
+    private final Point mMoveWindowOffset = new Point();
+
+    private View mMirrorView;
+    private SurfaceView mMirrorSurfaceView;
+    private View mControlsView;
+    private View mOverlayView;
+
+    private MoveMirrorRunnable mMoveMirrorRunnable = new MoveMirrorRunnable();
+
+    WindowMagnificationController(Context context, Handler handler) {
+        mContext = context;
+        mHandler = handler;
+        Display display = mContext.getDisplay();
+        display.getSize(mDisplaySize);
+        mDisplayId = mContext.getDisplayId();
+
+        mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+
+        Resources r = context.getResources();
+        mBorderSize = (int) r.getDimension(R.dimen.magnification_border_size);
+        mMoveFrameAmountShort = (int) r.getDimension(R.dimen.magnification_frame_move_short);
+        mMoveFrameAmountLong = (int) r.getDimension(R.dimen.magnification_frame_move_long);
+
+        mScale = r.getInteger(R.integer.magnification_default_scale);
+    }
+
+    /**
+     * Creates a magnification window if it doesn't already exist.
+     */
+    void createWindowMagnification() {
+        if (mMirrorView != null) {
+            return;
+        }
+        createOverlayWindow();
+    }
+
+    private void createOverlayWindow() {
+        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+                WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
+                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+                PixelFormat.TRANSPARENT);
+        params.gravity = Gravity.TOP | Gravity.LEFT;
+        params.token = new Binder();
+        params.setTitle(mContext.getString(R.string.magnification_overlay_title));
+
+        mOverlayView = new View(mContext);
+        mOverlayView.getViewTreeObserver().addOnWindowAttachListener(
+                new ViewTreeObserver.OnWindowAttachListener() {
+                    @Override
+                    public void onWindowAttached() {
+                        mOverlayView.getViewTreeObserver().removeOnWindowAttachListener(this);
+                        createMirrorWindow();
+                        createControls();
+                    }
+
+                    @Override
+                    public void onWindowDetached() {
+
+                    }
+                });
+
+        mOverlayView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                | View.SYSTEM_UI_FLAG_FULLSCREEN
+                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+
+        mWm.addView(mOverlayView, params);
+    }
+
+    /**
+     * Deletes the magnification window.
+     */
+    void deleteWindowMagnification() {
+        if (mMirrorSurface != null) {
+            mTransaction.remove(mMirrorSurface).apply();
+            mMirrorSurface = null;
+        }
+
+        if (mOverlayView != null) {
+            mWm.removeView(mOverlayView);
+            mOverlayView = null;
+        }
+
+        if (mMirrorView != null) {
+            mWm.removeView(mMirrorView);
+            mMirrorView = null;
+        }
+
+        if (mControlsView != null) {
+            mWm.removeView(mControlsView);
+            mControlsView = null;
+        }
+    }
+
+    private void createMirrorWindow() {
+        setInitialStartBounds();
+
+        // The window should be the size the mirrored surface will be but also add room for the
+        // border and the drag handle.
+        int dragViewHeight = (int) mContext.getResources().getDimension(
+                R.dimen.magnification_drag_view_height);
+        int windowWidth = mMagnificationFrame.width() + 2 * mBorderSize;
+        int windowHeight = mMagnificationFrame.height() + dragViewHeight + 2 * mBorderSize;
+
+        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+                windowWidth, windowHeight,
+                WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,
+                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+                PixelFormat.TRANSPARENT);
+        params.gravity = Gravity.TOP | Gravity.LEFT;
+        params.token = mOverlayView.getWindowToken();
+        params.x = mMagnificationFrame.left;
+        params.y = mMagnificationFrame.top;
+        params.setTitle(mContext.getString(R.string.magnification_window_title));
+
+        mMirrorView = LayoutInflater.from(mContext).inflate(R.layout.window_magnifier_view, null);
+        mMirrorSurfaceView = mMirrorView.findViewById(R.id.surface_view);
+        // This places the SurfaceView's SurfaceControl above the ViewRootImpl's SurfaceControl to
+        // ensure the mirrored area can get touch instead of going to the window
+        mMirrorSurfaceView.setZOrderOnTop(true);
+
+        mMirrorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                | View.SYSTEM_UI_FLAG_FULLSCREEN
+                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
+        mWm.addView(mMirrorView, params);
+
+        SurfaceHolder holder = mMirrorSurfaceView.getHolder();
+        holder.addCallback(this);
+        holder.setFormat(PixelFormat.RGBA_8888);
+
+        addDragTouchListeners();
+    }
+
+    private void createControls() {
+        int controlsSize = (int) mContext.getResources().getDimension(
+                R.dimen.magnification_controls_size);
+
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(controlsSize, controlsSize,
+                WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
+                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+                PixelFormat.RGBA_8888);
+        lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+        lp.token = mOverlayView.getWindowToken();
+        lp.setTitle(mContext.getString(R.string.magnification_controls_title));
+
+        mControlsView = LayoutInflater.from(mContext).inflate(R.layout.magnifier_controllers, null);
+        mWm.addView(mControlsView, lp);
+
+        mLeftControl = mControlsView.findViewById(R.id.left_control);
+        mUpControl = mControlsView.findViewById(R.id.up_control);
+        mRightControl = mControlsView.findViewById(R.id.right_control);
+        mBottomControl = mControlsView.findViewById(R.id.down_control);
+
+        mLeftControl.setOnClickListener(this);
+        mUpControl.setOnClickListener(this);
+        mRightControl.setOnClickListener(this);
+        mBottomControl.setOnClickListener(this);
+
+        mLeftControl.setOnLongClickListener(this);
+        mUpControl.setOnLongClickListener(this);
+        mRightControl.setOnLongClickListener(this);
+        mBottomControl.setOnLongClickListener(this);
+
+        mLeftControl.setOnTouchListener(this);
+        mUpControl.setOnTouchListener(this);
+        mRightControl.setOnTouchListener(this);
+        mBottomControl.setOnTouchListener(this);
+    }
+
+    private void setInitialStartBounds() {
+        // Sets the initial frame area for the mirror and places it in the center of the display.
+        int initSize = Math.min(mDisplaySize.x, mDisplaySize.y) / 2;
+        int initX = mDisplaySize.x / 2 - initSize / 2;
+        int initY = mDisplaySize.y / 2 - initSize / 2;
+        mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize);
+    }
+
+    /**
+     * This is called once the surfaceView is created so the mirrored content can be placed as a
+     * child of the surfaceView.
+     */
+    private void createMirror() {
+        mMirrorSurface = WindowManagerWrapper.getInstance().mirrorDisplay(mDisplayId);
+        if (!mMirrorSurface.isValid()) {
+            return;
+        }
+        mTransaction.show(mMirrorSurface)
+                .reparent(mMirrorSurface, mMirrorSurfaceView.getSurfaceControl());
+
+        modifyWindowMagnification(mTransaction);
+        mTransaction.apply();
+    }
+
+    private void addDragTouchListeners() {
+        mDragView = mMirrorView.findViewById(R.id.drag_handle);
+        mLeftDrag = mMirrorView.findViewById(R.id.left_handle);
+        mTopDrag = mMirrorView.findViewById(R.id.top_handle);
+        mRightDrag = mMirrorView.findViewById(R.id.right_handle);
+        mBottomDrag = mMirrorView.findViewById(R.id.bottom_handle);
+
+        mDragView.setOnTouchListener(this);
+        mLeftDrag.setOnTouchListener(this);
+        mTopDrag.setOnTouchListener(this);
+        mRightDrag.setOnTouchListener(this);
+        mBottomDrag.setOnTouchListener(this);
+    }
+
+    /**
+     * Modifies the placement of the mirrored content.
+     */
+    private void modifyWindowMagnification(SurfaceControl.Transaction t) {
+        Rect sourceBounds = getSourceBounds(mMagnificationFrame, mScale);
+        // The final destination for the magnification surface should be at 0,0 since the
+        // ViewRootImpl's position will change
+        mTmpRect.set(0, 0, mMagnificationFrame.width(), mMagnificationFrame.height());
+
+        WindowManager.LayoutParams params =
+                (WindowManager.LayoutParams) mMirrorView.getLayoutParams();
+        params.x = mMagnificationFrame.left;
+        params.y = mMagnificationFrame.top;
+        mWm.updateViewLayout(mMirrorView, params);
+
+        t.setGeometry(mMirrorSurface, sourceBounds, mTmpRect, Surface.ROTATION_0);
+    }
+
+    @Override
+    public void onClick(View v) {
+        setMoveOffset(v, mMoveFrameAmountShort);
+        moveMirrorFromControls();
+    }
+
+    @Override
+    public boolean onLongClick(View v) {
+        mIsPressedDown = true;
+        setMoveOffset(v, mMoveFrameAmountLong);
+        mHandler.post(mMoveMirrorRunnable);
+        return true;
+    }
+
+    @Override
+    public boolean onTouch(View v, MotionEvent event) {
+        if (v == mLeftControl || v == mUpControl || v == mRightControl || v == mBottomControl) {
+            return handleControlTouchEvent(event);
+        } else if (v == mDragView || v == mLeftDrag || v == mTopDrag || v == mRightDrag
+                || v == mBottomDrag) {
+            return handleDragTouchEvent(event);
+        }
+        return false;
+    }
+
+    private boolean handleControlTouchEvent(MotionEvent event) {
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mIsPressedDown = false;
+                break;
+        }
+        return false;
+    }
+
+    private boolean handleDragTouchEvent(MotionEvent event) {
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mLastDrag.set(event.getRawX(), event.getRawY());
+                return true;
+            case MotionEvent.ACTION_MOVE:
+                int xDiff = (int) (event.getRawX() - mLastDrag.x);
+                int yDiff = (int) (event.getRawY() - mLastDrag.y);
+                mMagnificationFrame.offset(xDiff, yDiff);
+                mLastDrag.set(event.getRawX(), event.getRawY());
+                modifyWindowMagnification(mTransaction);
+                mTransaction.apply();
+                return true;
+        }
+        return false;
+    }
+
+    private void setMoveOffset(View v, int moveFrameAmount) {
+        mMoveWindowOffset.set(0, 0);
+
+        if (v == mLeftControl) {
+            mMoveWindowOffset.x = -moveFrameAmount;
+        } else if (v == mUpControl) {
+            mMoveWindowOffset.y = -moveFrameAmount;
+        } else if (v == mRightControl) {
+            mMoveWindowOffset.x = moveFrameAmount;
+        } else if (v == mBottomControl) {
+            mMoveWindowOffset.y = moveFrameAmount;
+        }
+    }
+
+    private void moveMirrorFromControls() {
+        mMagnificationFrame.offset(mMoveWindowOffset.x, mMoveWindowOffset.y);
+
+        modifyWindowMagnification(mTransaction);
+        mTransaction.apply();
+    }
+
+    /**
+     * Calculates the desired source bounds. This will be the area under from the center of  the
+     * displayFrame, factoring in scale.
+     */
+    private Rect getSourceBounds(Rect displayFrame, float scale) {
+        int halfWidth = displayFrame.width() / 2;
+        int halfHeight = displayFrame.height() / 2;
+        int left = displayFrame.left + (halfWidth - (int) (halfWidth / scale));
+        int right = displayFrame.right - (halfWidth - (int) (halfWidth / scale));
+        int top = displayFrame.top + (halfHeight - (int) (halfHeight / scale));
+        int bottom = displayFrame.bottom - (halfHeight - (int) (halfHeight / scale));
+        return new Rect(left, top, right, bottom);
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        createMirror();
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+    }
+
+    class MoveMirrorRunnable implements Runnable {
+        @Override
+        public void run() {
+            if (mIsPressedDown) {
+                moveMirrorFromControls();
+                mHandler.postDelayed(mMoveMirrorRunnable, 100);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS b/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS
new file mode 100644
index 0000000..8765c9a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+kchyn@google.com
+jaggies@google.com
+curtislb@google.com
+ilyamaty@google.com
+joshmccloskey@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 1277736..c82bc30 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -103,7 +103,6 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.function.Consumer;
 
 import javax.inject.Inject;
@@ -735,7 +734,7 @@
     @SuppressWarnings("FieldCanBeLocal")
     private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
         @Override
-        public void onPendingEntryAdded(NotificationEntry entry) {
+        public void onNotificationAdded(NotificationEntry entry) {
             boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
             boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
             boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 21471ec..4c1cf49 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -32,6 +32,9 @@
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -39,8 +42,11 @@
 import android.provider.Settings;
 import android.util.Log;
 
+import com.android.internal.graphics.ColorUtils;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.people.PeopleHubNotificationListenerKt;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -227,15 +233,29 @@
         List<Person> personList = getPeopleFromNotification(entry);
         if (personList.size() > 0) {
             final Person person = personList.get(0);
-
             if (person != null) {
                 icon = person.getIcon();
+                if (icon == null) {
+                    // Lets try and grab the icon constructed by the layout
+                    Drawable d = PeopleHubNotificationListenerKt.extractAvatarFromRow(entry);
+                    if (d instanceof  BitmapDrawable) {
+                        icon = Icon.createWithBitmap(((BitmapDrawable) d).getBitmap());
+                    }
+                }
             }
         }
         if (icon == null) {
-            icon = notification.getLargeIcon() != null
-                    ? notification.getLargeIcon()
-                    : notification.getSmallIcon();
+            boolean shouldTint = notification.getLargeIcon() == null;
+            icon = shouldTint
+                    ? notification.getSmallIcon()
+                    : notification.getLargeIcon();
+            if (shouldTint) {
+                int notifColor = entry.getSbn().getNotification().color;
+                notifColor = ColorUtils.setAlphaComponent(notifColor, 255);
+                notifColor = ContrastColorUtil.findContrastColor(notifColor, Color.WHITE,
+                        true /* findFg */, 3f);
+                icon.setTint(notifColor);
+            }
         }
         if (intent != null) {
             return new Notification.BubbleMetadata.Builder()
@@ -285,7 +305,7 @@
     }
 
     static boolean isShortcutIntent(PendingIntent intent) {
-        return intent.equals(sDummyShortcutIntent);
+        return intent != null && intent.equals(sDummyShortcutIntent);
     }
 
     static List<Person> getPeopleFromNotification(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 3cf14d6..99dd5e2 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -21,6 +21,7 @@
 import com.android.systemui.SizeCompatModeActivityController;
 import com.android.systemui.SliceBroadcastRelayHandler;
 import com.android.systemui.SystemUI;
+import com.android.systemui.accessibility.WindowMagnification;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.globalactions.GlobalActionsComponent;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -156,4 +157,10 @@
     @IntoMap
     @ClassKey(VolumeUI.class)
     public abstract SystemUI bindVolumeUI(VolumeUI sysui);
+
+    /** Inject into WindowMagnification. */
+    @Binds
+    @IntoMap
+    @ClassKey(WindowMagnification.class)
+    public abstract SystemUI bindWindowMagnification(WindowMagnification sysui);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 05a234f..d008e66 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -80,8 +80,7 @@
 
     public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager,
             DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
-            Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy,
-            DozeLog dozeLog) {
+            Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog) {
         mContext = context;
         mAlarmManager = alarmManager;
         mSensorManager = sensorManager;
@@ -154,7 +153,7 @@
         };
 
         mProximitySensor = new ProximitySensor(context.getResources(), sensorManager);
-
+        setProxListening(false);  // Don't immediately start listening when we register.
         mProximitySensor.register(
                 proximityEvent -> {
                     if (proximityEvent != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 2d6b946..722dc03 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -103,8 +103,7 @@
         mWakeLock = wakeLock;
         mAllowPulseTriggers = allowPulseTriggers;
         mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
-                config, wakeLock, this::onSensor, this::onProximityFar,
-                dozeParameters.getPolicy(), dozeLog);
+                config, wakeLock, this::onSensor, this::onProximityFar, dozeLog);
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
         mDockManager = dockManager;
         mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
index 9f79785..077d260 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.UserHandle;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -97,7 +98,7 @@
             if (!mReceiverRegistered) {
                 mCurrentUserId = ActivityManager.getCurrentUser();
                 IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
-                mBroadcastDispatcher.registerReceiver(this, filter);
+                mBroadcastDispatcher.registerReceiver(this, filter, null, UserHandle.ALL);
                 mReceiverRegistered = true;
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
index 9ae3882..ec1efa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
@@ -24,6 +24,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Represents a set of grouped notifications. The final notification list is usually a mix of
@@ -57,15 +58,22 @@
 
     @VisibleForTesting
     public void setSummary(@Nullable NotificationEntry summary) {
-        mSummary = summary;
+        if (!Objects.equals(mSummary, summary)) {
+            mSummary = summary;
+            onGroupingUpdated();
+        }
     }
 
     void clearChildren() {
-        mChildren.clear();
+        if (mChildren.size() != 0) {
+            mChildren.clear();
+            onGroupingUpdated();
+        }
     }
 
     void addChild(NotificationEntry child) {
         mChildren.add(child);
+        onGroupingUpdated();
     }
 
     void sortChildren(Comparator<? super NotificationEntry> c) {
@@ -77,4 +85,5 @@
     }
 
     public static final GroupEntry ROOT_ENTRY = new GroupEntry("<root>");
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index 6ce7fd9..052473a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -19,6 +19,14 @@
 import android.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.notification.collection.provider.DerivedMember;
+import com.android.systemui.statusbar.notification.collection.provider.IsHighPriorityProvider;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
 
 /**
  * Abstract superclass for top-level entries, i.e. things that can appear in the final notification
@@ -26,14 +34,23 @@
  */
 public abstract class ListEntry {
     private final String mKey;
+    private final IsHighPriorityProvider mIsHighPriorityProvider = new IsHighPriorityProvider();
+    private final List<DerivedMember> mDerivedMemberList = Arrays.asList(mIsHighPriorityProvider);
 
     @Nullable private GroupEntry mParent;
     @Nullable private GroupEntry mPreviousParent;
     private int mSection;
     int mFirstAddedIteration = -1;
 
+    // TODO: (b/145659174) remove groupManager when moving to NewNotifPipeline. Logic
+    //  replaced in GroupEntry and NotifListBuilderImpl
+    private final NotificationGroupManager mGroupManager;
+
     ListEntry(String key) {
         mKey = key;
+
+        // TODO: (b/145659174) remove
+        mGroupManager = Dependency.get(NotificationGroupManager.class);
     }
 
     public String getKey() {
@@ -53,7 +70,11 @@
 
     @VisibleForTesting
     public void setParent(@Nullable GroupEntry parent) {
-        mParent = parent;
+        if (!Objects.equals(mParent, parent)) {
+            invalidateParent();
+            mParent = parent;
+            onGroupingUpdated();
+        }
     }
 
     @Nullable public GroupEntry getPreviousParent() {
@@ -72,4 +93,58 @@
     void setSection(int section) {
         mSection = section;
     }
+
+    /**
+     * Resets the cached values of DerivedMembers.
+     */
+    void invalidateDerivedMembers() {
+        for (int i = 0; i < mDerivedMemberList.size(); i++) {
+            mDerivedMemberList.get(i).invalidate();
+        }
+    }
+
+    /**
+     * Whether this notification is shown to the user as a high priority notification: visible on
+     * the lock screen/status bar and in the top section in the shade.
+     */
+    public boolean isHighPriority() {
+        return mIsHighPriorityProvider.get(this);
+    }
+
+    private void invalidateParent() {
+        // invalidate our parent (GroupEntry) since DerivedMembers may be dependent on children
+        if (getParent() != null) {
+            getParent().invalidateDerivedMembers();
+        }
+
+        // TODO: (b/145659174) remove
+        final NotificationEntry notifEntry = getRepresentativeEntry();
+        if (notifEntry != null && mGroupManager.isGroupChild(notifEntry.getSbn())) {
+            NotificationEntry summary = mGroupManager.getLogicalGroupSummary(notifEntry.getSbn());
+            if (summary != null) {
+                summary.invalidateDerivedMembers();
+            }
+        }
+    }
+
+    void onGroupingUpdated() {
+        for (int i = 0; i < mDerivedMemberList.size(); i++) {
+            mDerivedMemberList.get(i).onGroupingUpdated();
+        }
+        invalidateParent();
+    }
+
+    void onSbnUpdated() {
+        for (int i = 0; i < mDerivedMemberList.size(); i++) {
+            mDerivedMemberList.get(i).onSbnUpdated();
+        }
+        invalidateParent();
+    }
+
+    void onRankingUpdated() {
+        for (int i = 0; i < mDerivedMemberList.size(); i++) {
+            mDerivedMemberList.get(i).onRankingUpdated();
+        }
+        invalidateParent();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 232fb6d..de16ef5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -21,7 +21,6 @@
 import static android.app.Notification.CATEGORY_EVENT;
 import static android.app.Notification.CATEGORY_MESSAGE;
 import static android.app.Notification.CATEGORY_REMINDER;
-import static android.app.Notification.EXTRA_MESSAGES;
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
@@ -42,7 +41,6 @@
 import android.content.Context;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
-import android.os.Parcelable;
 import android.os.SystemClock;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.SnoozeCriterion;
@@ -92,7 +90,6 @@
     private StatusBarNotification mSbn;
     private Ranking mRanking;
 
-
     /*
      * Bookkeeping members
      */
@@ -120,7 +117,6 @@
     public int targetSdk;
     private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
     public CharSequence remoteInputText;
-    private final List<Person> mAssociatedPeople = new ArrayList<>();
     private Notification.BubbleMetadata mBubbleMetadata;
 
     /**
@@ -157,12 +153,6 @@
      */
     private boolean hasSentReply;
 
-    /**
-     * Whether this notification is shown to the user as a high priority notification: visible on
-     * the lock screen/status bar and in the top section in the shade.
-     */
-    private boolean mHighPriority;
-
     private boolean mSensitive = true;
     private Runnable mOnSensitiveChangedListener;
     private boolean mAutoHeadsUp;
@@ -212,9 +202,11 @@
                     + " doesn't match existing key " + mKey);
         }
 
-        mSbn = sbn;
-        mBubbleMetadata = mSbn.getNotification().getBubbleMetadata();
-        updatePeopleList();
+        if (!Objects.equals(mSbn, sbn)) {
+            mSbn = sbn;
+            mBubbleMetadata = mSbn.getNotification().getBubbleMetadata();
+            onSbnUpdated();
+        }
     }
 
     /**
@@ -239,10 +231,12 @@
                     + " doesn't match existing key " + mKey);
         }
 
-        mRanking = ranking;
+        if (!Objects.equals(mRanking, ranking)) {
+            mRanking = ranking;
+            onRankingUpdated();
+        }
     }
 
-
     /*
      * Convenience getters for SBN and Ranking members
      */
@@ -304,49 +298,10 @@
         return interruption;
     }
 
-    public boolean isHighPriority() {
-        return mHighPriority;
-    }
-
-    public void setIsHighPriority(boolean highPriority) {
-        this.mHighPriority = highPriority;
-    }
-
     public boolean isBubble() {
         return (mSbn.getNotification().flags & FLAG_BUBBLE) != 0;
     }
 
-    private void updatePeopleList() {
-        mAssociatedPeople.clear();
-
-        Bundle extras = mSbn.getNotification().extras;
-        if (extras == null) {
-            return;
-        }
-
-        List<Person> p = extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST);
-
-        if (p != null) {
-            mAssociatedPeople.addAll(p);
-        }
-
-        if (Notification.MessagingStyle.class.equals(
-                mSbn.getNotification().getNotificationStyle())) {
-            final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
-            if (!ArrayUtils.isEmpty(messages)) {
-                for (Notification.MessagingStyle.Message message :
-                        Notification.MessagingStyle.Message
-                                .getMessagesFromBundleArray(messages)) {
-                    mAssociatedPeople.add(message.getSenderPerson());
-                }
-            }
-        }
-    }
-
-    boolean hasAssociatedPeople() {
-        return mAssociatedPeople.size() > 0;
-    }
-
     /**
      * Returns the data needed for a bubble for this notification, if it exists.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 48a4882..7010943 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -16,14 +16,11 @@
 
 package com.android.systemui.statusbar.notification.collection
 
-import android.app.NotificationManager.IMPORTANCE_DEFAULT
 import android.app.NotificationManager.IMPORTANCE_HIGH
-import android.app.NotificationManager.IMPORTANCE_LOW
 import android.app.NotificationManager.IMPORTANCE_MIN
 import android.service.notification.NotificationListenerService.Ranking
 import android.service.notification.NotificationListenerService.RankingMap
 import android.service.notification.StatusBarNotification
-import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.statusbar.NotificationMediaManager
 import com.android.systemui.statusbar.notification.NotificationFilter
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
@@ -105,44 +102,6 @@
         return entry.key == mediaManager.mediaNotificationKey && importance > IMPORTANCE_MIN
     }
 
-    @VisibleForTesting
-    protected fun isHighPriority(entry: NotificationEntry): Boolean {
-        if (entry.importance >= IMPORTANCE_DEFAULT ||
-                hasHighPriorityCharacteristics(entry)) {
-            return true
-        }
-
-        if (groupManager.isSummaryOfGroup(entry.sbn)) {
-            val logicalChildren = groupManager.getLogicalChildren(entry.sbn)
-            for (child in logicalChildren) {
-                if (isHighPriority(child)) {
-                    return true
-                }
-            }
-        }
-
-        return false
-    }
-
-    private fun hasHighPriorityCharacteristics(entry: NotificationEntry): Boolean {
-        val c = entry.channel
-        val n = entry.sbn.notification
-
-        if ((n.isForegroundService && entry.ranking.importance >= IMPORTANCE_LOW) ||
-                n.hasMediaSession() ||
-                entry.isPeopleNotification()) {
-            // Users who have long pressed and demoted to silent should not see the notification
-            // in the top section
-            if (c != null && c.hasUserSetImportance()) {
-                return false
-            }
-
-            return true
-        }
-
-        return false
-    }
-
     fun updateRanking(
         newRankingMap: RankingMap?,
         entries: Collection<NotificationEntry>,
@@ -219,7 +178,10 @@
                         // TODO: notify group manager here?
                         groupManager.onEntryUpdated(entry, oldSbn)
                     }
-                    entry.setIsHighPriority(isHighPriority(entry))
+
+                    // TODO: (b/145659174) remove after moving to new NotifPipeline
+                    // (should be able to remove all groupManager code post-migration)
+                    entry.invalidateDerivedMembers()
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index 4413dc4..232246e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -37,6 +37,8 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
@@ -133,8 +135,8 @@
                 // to be shown on the lockscreen
                 // TODO: grouping hasn't happened yet (b/145134683)
                 if (entry.getParent() != null) {
-                    final NotificationEntry summary = entry.getParent().getRepresentativeEntry();
-                    if (priorityExceedsLockscreenShowingThreshold(summary)) {
+                    final GroupEntry parent = entry.getParent();
+                    if (priorityExceedsLockscreenShowingThreshold(parent)) {
                         return false;
                     }
                 }
@@ -144,7 +146,7 @@
         }
     };
 
-    private boolean priorityExceedsLockscreenShowingThreshold(NotificationEntry entry) {
+    private boolean priorityExceedsLockscreenShowingThreshold(ListEntry entry) {
         if (entry == null) {
             return false;
         }
@@ -154,7 +156,7 @@
             //  correctly updated before reaching this point (b/145134683)
             return entry.isHighPriority();
         } else {
-            return !entry.getRanking().isAmbient();
+            return !entry.getRepresentativeEntry().getRanking().isAmbient();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DerivedMember.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DerivedMember.java
new file mode 100644
index 0000000..815e6f7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DerivedMember.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.provider;
+/**
+ * Caches a computed value until invalidate() is called
+ * @param <Parent> Object used to computeValue
+ * @param <Value> type of value to cache until invalidate is called
+ */
+public abstract class DerivedMember<Parent, Value> {
+    private Value mValue;
+    protected abstract Value computeValue(Parent parent);
+
+    /**
+     * Gets the last cached value, else recomputes the value.
+     */
+    public Value get(Parent parent) {
+        if (mValue == null) {
+            mValue = computeValue(parent);
+        }
+        return mValue;
+    }
+
+    /**
+     * Resets the cached value.
+     * Next time "get" is called, the value is recomputed.
+     */
+    public void invalidate() {
+        mValue = null;
+    }
+
+    /**
+     * Called when a NotificationEntry's status bar notification has updated.
+     * Derived members can invalidate here.
+     */
+    public void onSbnUpdated() {}
+
+    /**
+     * Called when a NotificationEntry's Ranking has updated.
+     * Derived members can invalidate here.
+     */
+    public void onRankingUpdated() {}
+
+    /**
+     * Called when a ListEntry's grouping information (parent or children) has changed.
+     * Derived members can invalidate here.
+     */
+    public void onGroupingUpdated() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProvider.java
new file mode 100644
index 0000000..76e256b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProvider.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.provider;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.Person;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Whether the ListEntry is shown to the user as a high priority notification: visible on
+ * the lock screen/status bar and in the top section in the shade.
+ *
+ * A NotificationEntry is considered high priority if it:
+ *  - has importance greater than or equal to IMPORTANCE_DEFAULT
+ *  OR
+ *  - their importance has NOT been set to a low priority option by the user AND the notification
+ *  fulfills one of the following:
+ *      - has a person associated with it
+ *      - has a media session associated with it
+ *      - has messaging style
+ *
+ * A GroupEntry is considered high priority if its representativeEntry (summary) or children are
+ * high priority
+ */
+public class IsHighPriorityProvider extends DerivedMember<ListEntry, Boolean> {
+    // TODO: (b/145659174) remove groupManager when moving to NewNotifPipeline. Logic
+    //  replaced in GroupEntry and NotifListBuilderImpl
+    private final NotificationGroupManager mGroupManager;
+
+
+    public IsHighPriorityProvider() {
+        // TODO: (b/145659174) remove
+        mGroupManager = Dependency.get(NotificationGroupManager.class);
+    }
+
+    @Override
+    protected Boolean computeValue(ListEntry entry) {
+        if (entry == null) {
+            return false;
+        }
+
+        return isHighPriority(entry);
+    }
+
+    private boolean isHighPriority(ListEntry listEntry) {
+        // requires groups have been set (AFTER PipelineState.STATE_TRANSFORMING)
+        final NotificationEntry notifEntry = listEntry.getRepresentativeEntry();
+        return notifEntry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
+                || hasHighPriorityCharacteristics(notifEntry)
+                || hasHighPriorityChild(listEntry);
+
+    }
+
+    private boolean hasHighPriorityChild(ListEntry entry) {
+        // TODO: (b/145659174) remove
+        if (entry instanceof NotificationEntry) {
+            NotificationEntry notifEntry = (NotificationEntry) entry;
+            if (mGroupManager.isSummaryOfGroup(notifEntry.getSbn())) {
+                List<NotificationEntry> logicalChildren =
+                        mGroupManager.getLogicalChildren(notifEntry.getSbn());
+                for (NotificationEntry child : logicalChildren) {
+                    if (child.isHighPriority()) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        if (entry instanceof GroupEntry) {
+            for (NotificationEntry child : ((GroupEntry) entry).getChildren()) {
+                if (child.isHighPriority()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean hasHighPriorityCharacteristics(NotificationEntry entry) {
+        return !hasUserSetImportance(entry)
+                && (isImportantOngoing(entry)
+                || entry.getSbn().getNotification().hasMediaSession()
+                || hasPerson(entry)
+                || isMessagingStyle(entry));
+    }
+
+    private boolean isImportantOngoing(NotificationEntry entry) {
+        return entry.getSbn().getNotification().isForegroundService()
+                && entry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_LOW;
+    }
+
+    private boolean isMessagingStyle(NotificationEntry entry) {
+        return Notification.MessagingStyle.class.equals(
+                entry.getSbn().getNotification().getNotificationStyle());
+    }
+
+    private boolean hasPerson(NotificationEntry entry) {
+        // TODO: cache favorite and recent contacts to check contact affinity
+        Notification notification = entry.getSbn().getNotification();
+        ArrayList<Person> people = notification.extras != null
+                ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
+                : new ArrayList<>();
+        return people != null && !people.isEmpty();
+    }
+
+    private boolean hasUserSetImportance(NotificationEntry entry) {
+        return entry.getRanking().getChannel() != null
+                && entry.getRanking().getChannel().hasUserSetImportance();
+    }
+
+    @Override
+    public void onSbnUpdated() {
+        invalidate();
+    }
+
+    @Override
+    public void onRankingUpdated() {
+        invalidate();
+    }
+
+    @Override
+    public void onGroupingUpdated() {
+        invalidate();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
index 987b52db..784673e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
@@ -249,7 +249,7 @@
     }
 }
 
-private fun extractAvatarFromRow(entry: NotificationEntry): Drawable? =
+fun extractAvatarFromRow(entry: NotificationEntry): Drawable? =
         entry.row
                 ?.childrenWithId(R.id.expanded)
                 ?.mapNotNull { it as? ViewGroup }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index f3e9b6b..183adeb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -37,12 +37,19 @@
 import android.os.UserHandle;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Dependency;
+import com.android.systemui.DumpController;
+import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.qualifiers.MainHandler;
 import com.android.systemui.statusbar.NotificationMediaManager;
 
 import libcore.io.IoUtils;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.Objects;
 
 import javax.inject.Inject;
@@ -52,16 +59,15 @@
  * Manages the lockscreen wallpaper.
  */
 @Singleton
-public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implements Runnable {
+public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implements Runnable,
+        Dumpable {
 
     private static final String TAG = "LockscreenWallpaper";
 
-    private final NotificationMediaManager mMediaManager =
-            Dependency.get(NotificationMediaManager.class);
-
+    private final NotificationMediaManager mMediaManager;
     private final WallpaperManager mWallpaperManager;
-    private Handler mH;
     private final KeyguardUpdateMonitor mUpdateMonitor;
+    private final Handler mH;
 
     private boolean mCached;
     private Bitmap mCache;
@@ -74,10 +80,16 @@
     @Inject
     public LockscreenWallpaper(WallpaperManager wallpaperManager,
             @Nullable IWallpaperManager iWallpaperManager,
-            KeyguardUpdateMonitor keyguardUpdateMonitor) {
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            DumpController dumpController,
+            NotificationMediaManager mediaManager,
+            @MainHandler Handler mainHandler) {
+        dumpController.registerDumpable(getClass().getSimpleName(), this);
         mWallpaperManager = wallpaperManager;
         mCurrentUserId = ActivityManager.getCurrentUser();
         mUpdateMonitor = keyguardUpdateMonitor;
+        mMediaManager = mediaManager;
+        mH = mainHandler;
 
         if (iWallpaperManager != null) {
             // Service is disabled on some devices like Automotive
@@ -89,14 +101,6 @@
         }
     }
 
-    void setHandler(Handler handler) {
-        if (mH != null) {
-            Log.wtfStack(TAG, "Handler has already been set. Trying to double initialize?");
-            return;
-        }
-        mH = handler;
-    }
-
     public Bitmap getBitmap() {
         if (mCached) {
             return mCache;
@@ -227,6 +231,16 @@
         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println(getClass().getSimpleName() + ":");
+        IndentingPrintWriter iPw = new IndentingPrintWriter(pw, "  ").increaseIndent();
+        iPw.println("mCached=" + mCached);
+        iPw.println("mCache=" + mCache);
+        iPw.println("mCurrentUserId=" + mCurrentUserId);
+        iPw.println("mSelectedUser=" + mSelectedUser);
+    }
+
     private static class LoaderResult {
         public final boolean success;
         public final Bitmap bitmap;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 1454e25..4c5bbce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -360,7 +360,8 @@
             return false;
         }
 
-        if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()) {
+        if (mState == ScrimState.AOD
+                && (mDozeParameters.getAlwaysOn() || mDockManager.isDocked())) {
             return true;
         }
 
@@ -560,7 +561,7 @@
     }
 
     protected void scheduleUpdate() {
-        if (mUpdatePending) return;
+        if (mUpdatePending || mScrimBehind == null) return;
 
         // Make sure that a frame gets scheduled.
         mScrimBehind.invalidate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index a9d7601..7e32581 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1061,7 +1061,6 @@
 
         if (ENABLE_LOCKSCREEN_WALLPAPER && mWallpaperSupported) {
             mLockscreenWallpaper = mLockscreenWallpaperLazy.get();
-            mLockscreenWallpaper.setHandler(mHandler);
         }
 
         mKeyguardIndicationController =
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
new file mode 100644
index 0000000..62ae7b9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
@@ -0,0 +1,717 @@
+/*
+ * 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.util.animation
+
+import android.os.Looper
+import android.util.ArrayMap
+import android.util.Log
+import android.view.View
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.FlingAnimation
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.dynamicanimation.animation.SpringAnimation
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.systemui.util.animation.PhysicsAnimator.Companion.getInstance
+import java.util.WeakHashMap
+
+/**
+ * Extension function for all objects which will return a PhysicsAnimator instance for that object.
+ */
+val <T : View> T.physicsAnimator: PhysicsAnimator<T> get() { return getInstance(this) }
+
+private const val TAG = "PhysicsAnimator"
+
+typealias EndAction = () -> Unit
+
+/** A map of Property -> AnimationUpdate, which is provided to update listeners on each frame. */
+typealias UpdateMap<T> =
+        ArrayMap<FloatPropertyCompat<in T>, PhysicsAnimator.AnimationUpdate>
+
+/**
+ * Map of the animators associated with a given object. This ensures that only one animator
+ * per object exists.
+ */
+internal val animators = WeakHashMap<Any, PhysicsAnimator<*>>()
+
+/**
+ * Default spring configuration to use for animations where stiffness and/or damping ratio
+ * were not provided.
+ */
+private val defaultSpring = PhysicsAnimator.SpringConfig(
+        SpringForce.STIFFNESS_MEDIUM,
+        SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
+
+/** Default fling configuration to use for animations where friction was not provided. */
+private val defaultFling = PhysicsAnimator.FlingConfig(
+        friction = 1f, min = -Float.MAX_VALUE, max = Float.MAX_VALUE)
+
+/** Whether to log helpful debug information about animations. */
+private var verboseLogging = false
+
+/**
+ * Animator that uses physics-based animations to animate properties on views and objects. Physics
+ * animations use real-world physical concepts, such as momentum and mass, to realistically simulate
+ * motion. PhysicsAnimator is heavily inspired by [android.view.ViewPropertyAnimator], and
+ * also uses the builder pattern to configure and start animations.
+ *
+ * The physics animations are backed by [DynamicAnimation].
+ *
+ * @param T The type of the object being animated.
+ */
+class PhysicsAnimator<T> private constructor (val target: T) {
+
+    /** Data class for representing animation frame updates. */
+    data class AnimationUpdate(val value: Float, val velocity: Float)
+
+    /** [DynamicAnimation] instances for the given properties. */
+    private val springAnimations = ArrayMap<FloatPropertyCompat<in T>, SpringAnimation>()
+    private val flingAnimations = ArrayMap<FloatPropertyCompat<in T>, FlingAnimation>()
+
+    /**
+     * Spring and fling configurations for the properties to be animated on the target. We'll
+     * configure and start the DynamicAnimations for these properties according to the provided
+     * configurations.
+     */
+    private val springConfigs = ArrayMap<FloatPropertyCompat<in T>, SpringConfig>()
+    private val flingConfigs = ArrayMap<FloatPropertyCompat<in T>, FlingConfig>()
+
+    /**
+     * Animation listeners for the animation. These will be notified when each property animation
+     * updates or ends.
+     */
+    private val updateListeners = ArrayList<UpdateListener<T>>()
+    private val endListeners = ArrayList<EndListener<T>>()
+
+    /** End actions to run when all animations have completed.  */
+    private val endActions = ArrayList<EndAction>()
+
+    /**
+     * Internal listeners that respond to DynamicAnimations updating and ending, and dispatch to
+     * the listeners provided via [addUpdateListener] and [addEndListener]. This allows us to add
+     * just one permanent update and end listener to the DynamicAnimations.
+     */
+    internal var internalListeners = ArrayList<InternalListener>()
+
+    /**
+     * Action to run when [start] is called. This can be changed by
+     * [PhysicsAnimatorTestUtils.prepareForTest] to enable animators to run under test and provide
+     * helpful test utilities.
+     */
+    internal var startAction: () -> Unit = ::startInternal
+
+    /**
+     * Springs a property to the given value, using the provided configuration settings.
+     *
+     * Springs are used when you know the exact value to which you want to animate. They can be
+     * configured with a start velocity (typically used when the spring is initiated by a touch
+     * event), but this velocity will be realistically attenuated as forces are applied to move the
+     * property towards the end value.
+     *
+     * If you find yourself repeating the same stiffness and damping ratios many times, consider
+     * storing a single [SpringConfig] instance and passing that in instead of individual values.
+     *
+     * @param property The property to spring to the given value. The property must be an instance
+     * of FloatPropertyCompat&lt;? super T&gt;. For example, if this is a
+     * PhysicsAnimator&lt;FrameLayout&gt;, you can use a FloatPropertyCompat&lt;FrameLayout&gt;, as
+     * well as a FloatPropertyCompat&lt;ViewGroup&gt;, and so on.
+     * @param toPosition The value to spring the given property to.
+     * @param startVelocity The initial velocity to use for the animation.
+     * @param stiffness The stiffness to use for the spring. Higher stiffness values result in
+     * faster animations, while lower stiffness means a slower animation. Reasonable values for
+     * low, medium, and high stiffness can be found as constants in [SpringForce].
+     * @param dampingRatio The damping ratio (bounciness) to use for the spring. Higher values
+     * result in a less 'springy' animation, while lower values allow the animation to bounce
+     * back and forth for a longer time after reaching the final position. Reasonable values for
+     * low, medium, and high damping can be found in [SpringForce].
+     */
+    fun spring(
+        property: FloatPropertyCompat<in T>,
+        toPosition: Float,
+        startVelocity: Float = 0f,
+        stiffness: Float = defaultSpring.stiffness,
+        dampingRatio: Float = defaultSpring.dampingRatio
+    ): PhysicsAnimator<T> {
+        if (verboseLogging) {
+            Log.d(TAG, "Springing ${getReadablePropertyName(property)} to $toPosition.")
+        }
+
+        springConfigs[property] =
+                SpringConfig(stiffness, dampingRatio, startVelocity, toPosition)
+        return this
+    }
+
+    /**
+     * Springs a property to a given value using the provided start velocity and configuration
+     * options.
+     *
+     * @see spring
+     */
+    fun spring(
+        property: FloatPropertyCompat<in T>,
+        toPosition: Float,
+        startVelocity: Float,
+        config: SpringConfig = defaultSpring
+    ): PhysicsAnimator<T> {
+        return spring(
+                property, toPosition, startVelocity, config.stiffness, config.dampingRatio)
+    }
+
+    /**
+     * Springs a property to a given value using the provided configuration options, and a start
+     * velocity of 0f.
+     *
+     * @see spring
+     */
+    fun spring(
+        property: FloatPropertyCompat<in T>,
+        toPosition: Float,
+        config: SpringConfig = defaultSpring
+    ): PhysicsAnimator<T> {
+        return spring(property, toPosition, 0f, config)
+    }
+
+    /**
+     * Flings a property using the given start velocity, using a [FlingAnimation] configured using
+     * the provided configuration settings.
+     *
+     * Flings are used when you have a start velocity, and want the property value to realistically
+     * decrease as friction is applied until the velocity reaches zero. Flings do not have a
+     * deterministic end value. If you are attempting to animate to a specific end value, use
+     * [spring].
+     *
+     * If you find yourself repeating the same friction/min/max values, consider storing a single
+     * [FlingConfig] and passing that in instead.
+     *
+     * @param property The property to fling using the given start velocity.
+     * @param startVelocity The start velocity (in pixels per second) with which to start the fling.
+     * @param friction Friction value applied to slow down the animation over time. Higher values
+     * will more quickly slow the animation. Typical friction values range from 1f to 10f.
+     * @param min The minimum value allowed for the animation. If this value is reached, the
+     * animation will end abruptly.
+     * @param max The maximum value allowed for the animation. If this value is reached, the
+     * animation will end abruptly.
+     */
+    fun fling(
+        property: FloatPropertyCompat<in T>,
+        startVelocity: Float,
+        friction: Float = defaultFling.friction,
+        min: Float = defaultFling.min,
+        max: Float = defaultFling.max
+    ): PhysicsAnimator<T> {
+        if (verboseLogging) {
+            Log.d(TAG, "Flinging ${getReadablePropertyName(property)} " +
+                    "with velocity $startVelocity.")
+        }
+
+        flingConfigs[property] = FlingConfig(friction, min, max, startVelocity)
+        return this
+    }
+
+    /**
+     * Flings a property using the given start velocity, using a [FlingAnimation] configured using
+     * the provided configuration settings.
+     *
+     * @see fling
+     */
+    fun fling(
+        property: FloatPropertyCompat<in T>,
+        startVelocity: Float,
+        config: FlingConfig = defaultFling
+    ): PhysicsAnimator<T> {
+        return fling(property, startVelocity, config.friction, config.min, config.max)
+    }
+
+    /**
+     * Adds a listener that will be called whenever any property on the animated object is updated.
+     * This will be called on every animation frame, with the current value of the animated object
+     * and the new property values.
+     */
+    fun addUpdateListener(listener: UpdateListener<T>): PhysicsAnimator<T> {
+        updateListeners.add(listener)
+        return this
+    }
+
+    /**
+     * Adds a listener that will be called whenever a property's animation ends. This is useful if
+     * you care about a specific property ending, or want to use the end value/end velocity from a
+     * particular property's animation. If you just want to run an action when all property
+     * animations have ended, use [withEndActions].
+     */
+    fun addEndListener(listener: EndListener<T>): PhysicsAnimator<T> {
+        endListeners.add(listener)
+        return this
+    }
+
+    /**
+     * Adds end actions that will be run sequentially when animations for every property involved in
+     * this specific animation have ended (unless they were explicitly canceled). For example, if
+     * you call:
+     *
+     * animator
+     *   .spring(TRANSLATION_X, ...)
+     *   .spring(TRANSLATION_Y, ...)
+     *   .withEndAction(action)
+     *   .start()
+     *
+     * 'action' will be run when both TRANSLATION_X and TRANSLATION_Y end.
+     *
+     * Other properties may still be animating, if those animations were not started in the same
+     * call. For example:
+     *
+     * animator
+     *   .spring(ALPHA, ...)
+     *   .start()
+     *
+     * animator
+     *   .spring(TRANSLATION_X, ...)
+     *   .spring(TRANSLATION_Y, ...)
+     *   .withEndAction(action)
+     *   .start()
+     *
+     * 'action' will still be run as soon as TRANSLATION_X and TRANSLATION_Y end, even if ALPHA is
+     * still animating.
+     *
+     * If you want to run actions as soon as a subset of property animations have ended, you want
+     * access to the animation's end value/velocity, or you want to run these actions even if the
+     * animation is explicitly canceled, use [addEndListener]. End listeners have an allEnded param,
+     * which indicates that all relevant animations have ended.
+     */
+    fun withEndActions(vararg endActions: EndAction): PhysicsAnimator<T> {
+        this.endActions.addAll(endActions)
+        return this
+    }
+
+    /** Starts the animations! */
+    fun start() {
+        startAction()
+    }
+
+    /**
+     * Starts the animations for real! This is typically called immediately by [start] unless this
+     * animator is under test.
+     */
+    internal fun startInternal() {
+        if (!Looper.getMainLooper().isCurrentThread) {
+            Log.e(TAG, "Animations can only be started on the main thread. If you are seeing " +
+                    "this message in a test, call PhysicsAnimatorTestUtils#prepareForTest in " +
+                    "your test setup.")
+        }
+
+        // Add an internal listener that will dispatch animation events to the provided listeners.
+        internalListeners.add(InternalListener(
+                getAnimatedProperties(),
+                ArrayList(updateListeners),
+                ArrayList(endListeners),
+                ArrayList(endActions)))
+
+        for ((property, config) in flingConfigs) {
+            val currentValue = property.getValue(target)
+
+            // If the fling is already out of bounds, don't start it.
+            if (currentValue <= config.min || currentValue >= config.max) {
+                continue
+            }
+
+            val flingAnim = getFlingAnimation(property)
+            config.applyToAnimation(flingAnim)
+            flingAnim.start()
+        }
+
+        for ((property, config) in springConfigs) {
+            val springAnim = getSpringAnimation(property)
+            config.applyToAnimation(springAnim)
+            springAnim.start()
+        }
+
+        clearAnimator()
+    }
+
+    /** Clear the animator's builder variables. */
+    private fun clearAnimator() {
+        springConfigs.clear()
+        flingConfigs.clear()
+
+        updateListeners.clear()
+        endListeners.clear()
+        endActions.clear()
+    }
+
+    /** Retrieves a spring animation for the given property, building one if needed. */
+    private fun getSpringAnimation(property: FloatPropertyCompat<in T>): SpringAnimation {
+        return springAnimations.getOrPut(
+                property,
+                { configureDynamicAnimation(SpringAnimation(target, property), property)
+                        as SpringAnimation })
+    }
+
+    /** Retrieves a fling animation for the given property, building one if needed. */
+    private fun getFlingAnimation(property: FloatPropertyCompat<in T>): FlingAnimation {
+        return flingAnimations.getOrPut(
+                property,
+                { configureDynamicAnimation(FlingAnimation(target, property), property)
+                        as FlingAnimation })
+    }
+
+    /**
+     * Adds update and end listeners to the DynamicAnimation which will dispatch to the internal
+     * listeners.
+     */
+    private fun configureDynamicAnimation(
+        anim: DynamicAnimation<*>,
+        property: FloatPropertyCompat<in T>
+    ): DynamicAnimation<*> {
+        anim.addUpdateListener { _, value, velocity ->
+            for (i in 0 until internalListeners.size) {
+                internalListeners[i].onInternalAnimationUpdate(property, value, velocity)
+            }
+        }
+        anim.addEndListener { _, canceled, value, velocity ->
+            internalListeners.removeAll {
+                it.onInternalAnimationEnd(property, canceled, value, velocity) } }
+        return anim
+    }
+
+    /**
+     * Internal listener class that receives updates from DynamicAnimation listeners, and dispatches
+     * them to the appropriate update/end listeners. This class is also aware of which properties
+     * were being animated when the end listeners were passed in, so that we can provide the
+     * appropriate value for allEnded to [EndListener.onAnimationEnd].
+     */
+    internal inner class InternalListener constructor(
+        private var properties: Set<FloatPropertyCompat<in T>>,
+        private var updateListeners: List<UpdateListener<T>>,
+        private var endListeners: List<EndListener<T>>,
+        private var endActions: List<EndAction>
+    ) {
+
+        /** The number of properties whose animations haven't ended. */
+        private var numPropertiesAnimating = properties.size
+
+        /**
+         * Update values that haven't yet been dispatched because not all property animations have
+         * updated yet.
+         */
+        private val undispatchedUpdates =
+                ArrayMap<FloatPropertyCompat<in T>, AnimationUpdate>()
+
+        /** Called when a DynamicAnimation updates.  */
+        internal fun onInternalAnimationUpdate(
+            property: FloatPropertyCompat<in T>,
+            value: Float,
+            velocity: Float
+        ) {
+
+            // If this property animation isn't relevant to this listener, ignore it.
+            if (!properties.contains(property)) {
+                return
+            }
+
+            undispatchedUpdates[property] = AnimationUpdate(value, velocity)
+            maybeDispatchUpdates()
+        }
+
+        /**
+         * Called when a DynamicAnimation ends.
+         *
+         * @return True if this listener should be removed from the list of internal listeners, so
+         * it no longer receives updates from DynamicAnimations.
+         */
+        internal fun onInternalAnimationEnd(
+            property: FloatPropertyCompat<in T>,
+            canceled: Boolean,
+            finalValue: Float,
+            finalVelocity: Float
+        ): Boolean {
+
+            // If this property animation isn't relevant to this listener, ignore it.
+            if (!properties.contains(property)) {
+                return false
+            }
+
+            // Dispatch updates if we have one for each property.
+            numPropertiesAnimating--
+            maybeDispatchUpdates()
+
+            // If we didn't have an update for each property, dispatch the update for the ending
+            // property. This guarantees that an update isn't sent for this property *after* we call
+            // onAnimationEnd for that property.
+            if (undispatchedUpdates.contains(property)) {
+                updateListeners.forEach { updateListener ->
+                    updateListener.onAnimationUpdateForProperty(
+                            target,
+                            UpdateMap<T>().also { it[property] = undispatchedUpdates[property] })
+                }
+
+                undispatchedUpdates.remove(property)
+            }
+
+            val allEnded = !arePropertiesAnimating(properties)
+            endListeners.forEach {
+                it.onAnimationEnd(target, property, canceled, finalValue, finalVelocity, allEnded) }
+
+            // If all of the animations that this listener cares about have ended, run the end
+            // actions unless the animation was canceled.
+            if (allEnded && !canceled) {
+                endActions.forEach { it() }
+            }
+
+            return allEnded
+        }
+
+        /**
+         * Dispatch undispatched values if we've received an update from each of the animating
+         * properties.
+         */
+        private fun maybeDispatchUpdates() {
+            if (undispatchedUpdates.size >= numPropertiesAnimating &&
+                    undispatchedUpdates.size > 0) {
+                updateListeners.forEach {
+                    it.onAnimationUpdateForProperty(target, ArrayMap(undispatchedUpdates))
+                }
+
+                undispatchedUpdates.clear()
+            }
+        }
+    }
+
+    /** Return true if any animations are running on the object.  */
+    fun isRunning(): Boolean {
+        return arePropertiesAnimating(springAnimations.keys.union(flingAnimations.keys))
+    }
+
+    /** Returns whether the given property is animating.  */
+    fun isPropertyAnimating(property: FloatPropertyCompat<in T>): Boolean {
+        return springAnimations[property]?.isRunning ?: false
+    }
+
+    /** Returns whether any of the given properties are animating.  */
+    fun arePropertiesAnimating(properties: Set<FloatPropertyCompat<in T>>): Boolean {
+        return properties.any { isPropertyAnimating(it) }
+    }
+
+    /** Return the set of properties that will begin animating upon calling [start]. */
+    internal fun getAnimatedProperties(): Set<FloatPropertyCompat<in T>> {
+        return springConfigs.keys.union(flingConfigs.keys)
+    }
+
+    /** Cancels all in progress animations on all properties. */
+    fun cancel() {
+        for (dynamicAnim in flingAnimations.values.union(springAnimations.values)) {
+            dynamicAnim.cancel()
+        }
+    }
+
+    /**
+     * Container object for spring animation configuration settings. This allows you to store
+     * default stiffness and damping ratio values in a single configuration object, which you can
+     * pass to [spring].
+     */
+    data class SpringConfig internal constructor(
+        internal var stiffness: Float,
+        internal var dampingRatio: Float,
+        internal var startVel: Float = 0f,
+        internal var finalPosition: Float = -Float.MAX_VALUE
+    ) {
+
+        constructor() :
+                this(defaultSpring.stiffness, defaultSpring.dampingRatio)
+
+        constructor(stiffness: Float, dampingRatio: Float) :
+                this(stiffness = stiffness, dampingRatio = dampingRatio, startVel = 0f)
+
+        /** Apply these configuration settings to the given SpringAnimation. */
+        internal fun applyToAnimation(anim: SpringAnimation) {
+            val springForce = anim.spring ?: SpringForce()
+            anim.spring = springForce.apply {
+                stiffness = this@SpringConfig.stiffness
+                dampingRatio = this@SpringConfig.dampingRatio
+                finalPosition = this@SpringConfig.finalPosition
+            }
+
+            if (startVel != 0f) anim.setStartVelocity(startVel)
+        }
+    }
+
+    /**
+     * Container object for fling animation configuration settings. This allows you to store default
+     * friction values (as well as optional min/max values) in a single configuration object, which
+     * you can pass to [fling] and related methods.
+     */
+    data class FlingConfig internal constructor(
+        internal var friction: Float,
+        internal var min: Float,
+        internal var max: Float,
+        internal var startVel: Float
+    ) {
+
+        constructor() : this(defaultFling.friction)
+
+        constructor(friction: Float) :
+                this(friction, defaultFling.min, defaultFling.max)
+
+        constructor(friction: Float, min: Float, max: Float) :
+                this(friction, min, max, startVel = 0f)
+
+        /** Apply these configuration settings to the given FlingAnimation. */
+        internal fun applyToAnimation(anim: FlingAnimation) {
+            anim.apply {
+                friction = this@FlingConfig.friction
+                setMinValue(min)
+                setMaxValue(max)
+                setStartVelocity(startVel)
+            }
+        }
+    }
+
+    /**
+     * Listener for receiving values from in progress animations. Used with
+     * [PhysicsAnimator.addUpdateListener].
+     *
+     * @param <T> The type of the object being animated.
+    </T> */
+    interface UpdateListener<T> {
+
+        /**
+         * Called on each animation frame with the target object, and a map of FloatPropertyCompat
+         * -> AnimationUpdate, containing the latest value and velocity for that property. When
+         * multiple properties are animating together, the map will typically contain one entry for
+         * each property. However, you should never assume that this is the case - when a property
+         * animation ends earlier than the others, you'll receive an UpdateMap containing only that
+         * property's final update. Subsequently, you'll only receive updates for the properties
+         * that are still animating.
+         *
+         * Always check that the map contains an update for the property you're interested in before
+         * accessing it.
+         *
+         * @param target The animated object itself.
+         * @param values Map of property to AnimationUpdate, which contains that property
+         * animation's latest value and velocity. You should never assume that a particular property
+         * is present in this map.
+         */
+        fun onAnimationUpdateForProperty(
+            target: T,
+            values: UpdateMap<T>
+        )
+    }
+
+    /**
+     * Listener for receiving callbacks when animations end.
+     *
+     * @param <T> The type of the object being animated.
+    </T> */
+    interface EndListener<T> {
+
+        /**
+         * Called with the final animation values as each property animation ends. This can be used
+         * to respond to specific property animations concluding (such as hiding a view when ALPHA
+         * ends, even if the corresponding TRANSLATION animations have not ended).
+         *
+         * If you just want to run an action when all of the property animations have ended, you can
+         * use [PhysicsAnimator.withEndActions].
+         *
+         * @param target The animated object itself.
+         * @param property The property whose animation has just ended.
+         * @param canceled Whether the animation was explicitly canceled before it naturally ended.
+         * @param finalValue The final value of the animated property.
+         * @param finalVelocity The final velocity (in pixels per second) of the ended animation.
+         * This is typically zero, unless this was a fling animation which ended abruptly due to
+         * reaching its configured min/max values.
+         * @param allRelevantPropertyAnimsEnded Whether all properties relevant to this end listener
+         * have ended. Relevant properties are those which were animated alongside the
+         * [addEndListener] call where this animator was passed in. For example:
+         *
+         * animator
+         *    .spring(TRANSLATION_X, 100f)
+         *    .spring(TRANSLATION_Y, 200f)
+         *    .withEndListener(firstEndListener)
+         *    .start()
+         *
+         * firstEndListener will be called first for TRANSLATION_X, with allEnded = false,
+         * because TRANSLATION_Y is still running. When TRANSLATION_Y ends, it'll be called with
+         * allEnded = true.
+         *
+         * If a subsequent call to start() is made with other properties, those properties are not
+         * considered relevant and allEnded will still equal true when only TRANSLATION_X and
+         * TRANSLATION_Y end. For example, if immediately after the prior example, while
+         * TRANSLATION_X and TRANSLATION_Y are still animating, we called:
+         *
+         * animator.
+         *    .spring(SCALE_X, 2f, stiffness = 10f) // That will take awhile...
+         *    .withEndListener(secondEndListener)
+         *    .start()
+         *
+         * firstEndListener will still be called with allEnded = true when TRANSLATION_X/Y end, even
+         * though SCALE_X is still animating. Similarly, secondEndListener will be called with
+         * allEnded = true as soon as SCALE_X ends, even if the translation animations are still
+         * running.
+         */
+        fun onAnimationEnd(
+            target: T,
+            property: FloatPropertyCompat<in T>,
+            canceled: Boolean,
+            finalValue: Float,
+            finalVelocity: Float,
+            allRelevantPropertyAnimsEnded: Boolean
+        )
+    }
+
+    companion object {
+
+        /**
+         * Constructor to use to for new physics animator instances in [getInstance]. This is
+         * typically the default constructor, but [PhysicsAnimatorTestUtils] can change it so that
+         * all code using the physics animator is given testable instances instead.
+         */
+        internal var instanceConstructor: (Any) -> PhysicsAnimator<*> = ::PhysicsAnimator
+
+        @JvmStatic
+        fun <T : Any> getInstance(target: T): PhysicsAnimator<T> {
+            if (!animators.containsKey(target)) {
+                animators[target] = instanceConstructor(target)
+            }
+
+            return animators[target] as PhysicsAnimator<T>
+        }
+
+        /**
+         * Set whether all physics animators should log a lot of information about animations.
+         * Useful for debugging!
+         */
+        @JvmStatic
+        fun setVerboseLogging(debug: Boolean) {
+            verboseLogging = debug
+        }
+
+        @JvmStatic
+        fun getReadablePropertyName(property: FloatPropertyCompat<*>): String {
+            return when (property) {
+                DynamicAnimation.TRANSLATION_X -> "translationX"
+                DynamicAnimation.TRANSLATION_Y -> "translationY"
+                DynamicAnimation.TRANSLATION_Z -> "translationZ"
+                DynamicAnimation.SCALE_X -> "scaleX"
+                DynamicAnimation.SCALE_Y -> "scaleY"
+                DynamicAnimation.ROTATION -> "rotation"
+                DynamicAnimation.ROTATION_X -> "rotationX"
+                DynamicAnimation.ROTATION_Y -> "rotationY"
+                DynamicAnimation.SCROLL_X -> "scrollX"
+                DynamicAnimation.SCROLL_Y -> "scrollY"
+                DynamicAnimation.ALPHA -> "alpha"
+                else -> "Custom FloatPropertyCompat instance"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt
new file mode 100644
index 0000000..a1f74eb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt
@@ -0,0 +1,457 @@
+/*
+ * 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.util.animation
+
+import android.os.Handler
+import android.os.Looper
+import android.util.ArrayMap
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import java.util.ArrayDeque
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+typealias UpdateMatcher = (PhysicsAnimator.AnimationUpdate) -> Boolean
+typealias UpdateFramesPerProperty<T> =
+        ArrayMap<FloatPropertyCompat<in T>, ArrayList<PhysicsAnimator.AnimationUpdate>>
+
+/**
+ * Utilities for testing code that uses [PhysicsAnimator].
+ *
+ * Start by calling [prepareForTest] at the beginning of each test - this will modify the behavior
+ * of all PhysicsAnimator instances so that they post animations to the main thread (so they don't
+ * crash). It'll also enable the use of the other static helper methods in this class, which you can
+ * use to do things like block the test until animations complete (so you can test end states), or
+ * verify keyframes.
+ */
+object PhysicsAnimatorTestUtils {
+    var timeoutMs: Long = 2000
+    private var startBlocksUntilAnimationsEnd = false
+    private val animationThreadHandler = Handler(Looper.getMainLooper())
+    private val allAnimatedObjects = HashSet<Any>()
+    private val animatorTestHelpers = HashMap<PhysicsAnimator<*>, AnimatorTestHelper<*>>()
+
+    /**
+     * Modifies the behavior of all [PhysicsAnimator] instances so that they post animations to the
+     * main thread, and report all of their
+     */
+    @JvmStatic
+    fun prepareForTest() {
+        val defaultConstructor = PhysicsAnimator.instanceConstructor
+        PhysicsAnimator.instanceConstructor = fun(target: Any): PhysicsAnimator<*> {
+            val animator = defaultConstructor(target)
+            allAnimatedObjects.add(target)
+            animatorTestHelpers[animator] = AnimatorTestHelper(animator)
+            return animator
+        }
+
+        timeoutMs = 2000
+        startBlocksUntilAnimationsEnd = false
+        allAnimatedObjects.clear()
+    }
+
+    @JvmStatic
+    fun tearDown() {
+        val latch = CountDownLatch(1)
+        animationThreadHandler.post {
+            animatorTestHelpers.keys.forEach { it.cancel() }
+            latch.countDown()
+        }
+
+        latch.await()
+
+        animatorTestHelpers.clear()
+        animators.clear()
+        allAnimatedObjects.clear()
+    }
+
+    /**
+     * Sets the maximum time (in milliseconds) to block the test thread while waiting for animations
+     * before throwing an exception.
+     */
+    @JvmStatic
+    fun setBlockTimeout(timeoutMs: Long) {
+        this.timeoutMs = timeoutMs
+    }
+
+    /**
+     * Sets whether all animations should block the test thread until they end. This is typically
+     * the desired behavior, since you can invoke code that runs an animation and then assert things
+     * about its end state.
+     */
+    @JvmStatic
+    fun setAllAnimationsBlock(block: Boolean) {
+        startBlocksUntilAnimationsEnd = block
+    }
+
+    /**
+     * Blocks the calling thread until animations of the given property on the target object end.
+     */
+    @JvmStatic
+    @Throws(InterruptedException::class)
+    fun <T : Any> blockUntilAnimationsEnd(
+        animator: PhysicsAnimator<T>,
+        vararg properties: FloatPropertyCompat<in T>
+    ) {
+        val animatingProperties = HashSet<FloatPropertyCompat<in T>>()
+        for (property in properties) {
+            if (animator.isPropertyAnimating(property)) {
+                animatingProperties.add(property)
+            }
+        }
+
+        if (animatingProperties.size > 0) {
+            val latch = CountDownLatch(animatingProperties.size)
+            getAnimationTestHelper(animator).addTestEndListener(
+                    object : PhysicsAnimator.EndListener<T> {
+                override fun onAnimationEnd(
+                    target: T,
+                    property: FloatPropertyCompat<in T>,
+                    canceled: Boolean,
+                    finalValue: Float,
+                    finalVelocity: Float,
+                    allRelevantPropertyAnimsEnded: Boolean
+                ) {
+                    if (animatingProperties.contains(property)) {
+                        latch.countDown()
+                    }
+                }
+            })
+
+            latch.await(timeoutMs, TimeUnit.MILLISECONDS)
+        }
+    }
+
+    /**
+     * Blocks the calling thread until all animations of the given property (on all target objects)
+     * have ended. Useful when you don't have access to the objects being animated, but still need
+     * to wait for them to end so that other testable side effects occur (such as update/end
+     * listeners).
+     */
+    @JvmStatic
+    @Throws(InterruptedException::class)
+    fun <T : Any> blockUntilAnimationsEnd(
+        properties: FloatPropertyCompat<in T>
+    ) {
+        for (target in allAnimatedObjects) {
+            try {
+                blockUntilAnimationsEnd(
+                        PhysicsAnimator.getInstance(target) as PhysicsAnimator<T>, properties)
+            } catch (e: ClassCastException) {
+                // Keep checking the other objects for ones whose types match the provided
+                // properties.
+            }
+        }
+    }
+
+    /**
+     * Blocks the calling thread until the first animation frame in which predicate returns true. If
+     * the given object isn't animating, returns without blocking.
+     */
+    @JvmStatic
+    @Throws(InterruptedException::class)
+    fun <T : Any> blockUntilFirstAnimationFrameWhereTrue(
+        animator: PhysicsAnimator<T>,
+        predicate: (T) -> Boolean
+    ) {
+        if (animator.isRunning()) {
+            val latch = CountDownLatch(1)
+            getAnimationTestHelper(animator).addTestUpdateListener(object : PhysicsAnimator
+            .UpdateListener<T> {
+                override fun onAnimationUpdateForProperty(
+                    target: T,
+                    values: UpdateMap<T>
+                ) {
+                    if (predicate(target)) {
+                        latch.countDown()
+                    }
+                }
+            })
+
+            latch.await(timeoutMs, TimeUnit.MILLISECONDS)
+        }
+    }
+
+    /**
+     * Verifies that the animator reported animation frame values to update listeners that satisfy
+     * the given matchers, in order. Not all frames need to satisfy a matcher - we'll run through
+     * all animation frames, and check them against the current predicate. If it returns false, we
+     * continue through the frames until it returns true, and then move on to the next matcher.
+     * Verification fails if we run out of frames while unsatisfied matchers remain.
+     *
+     * If verification is successful, all frames to this point are considered 'verified' and will be
+     * cleared. Subsequent calls to this method will start verification at the next animation frame.
+     *
+     * Example: Verify that an animation surpassed x = 50f before going negative.
+     * verifyAnimationUpdateFrames(
+     *    animator, TRANSLATION_X,
+     *    { u -> u.value > 50f },
+     *    { u -> u.value < 0f })
+     *
+     * Example: verify that an animation went backwards at some point while still being on-screen.
+     * verifyAnimationUpdateFrames(
+     *    animator, TRANSLATION_X,
+     *    { u -> u.velocity < 0f && u.value >= 0f })
+     *
+     * This method is intended to help you test longer, more complicated animations where it's
+     * critical that certain values were reached. Using this method to test short animations can
+     * fail due to the animation having fewer frames than provided matchers. For example, an
+     * animation from x = 1f to x = 5f might only have two frames, at x = 3f and x = 5f. The
+     * following would then fail despite it seeming logically sound:
+     *
+     * verifyAnimationUpdateFrames(
+     *    animator, TRANSLATION_X,
+     *    { u -> u.value > 1f },
+     *    { u -> u.value > 2f },
+     *    { u -> u.value > 3f })
+     *
+     * Tests might also fail if your matchers are too granular, such as this example test after an
+     * animation from x = 0f to x = 100f. It's unlikely there was a frame specifically between 2f
+     * and 3f.
+     *
+     * verifyAnimationUpdateFrames(
+     *    animator, TRANSLATION_X,
+     *    { u -> u.value > 2f && u.value < 3f },
+     *    { u -> u.value >= 50f })
+     *
+     * Failures will print a helpful log of all animation frames so you can see what caused the test
+     * to fail.
+     */
+    fun <T : Any> verifyAnimationUpdateFrames(
+        animator: PhysicsAnimator<T>,
+        property: FloatPropertyCompat<in T>,
+        firstUpdateMatcher: UpdateMatcher,
+        vararg additionalUpdateMatchers: UpdateMatcher
+    ) {
+        val updateFrames: UpdateFramesPerProperty<T> = getAnimationUpdateFrames(animator)
+        val matchers = ArrayDeque<UpdateMatcher>(
+                additionalUpdateMatchers.toList())
+        val frameTraceMessage = StringBuilder()
+
+        var curMatcher = firstUpdateMatcher
+
+        // Loop through the updates from the testable animator.
+        for (update in updateFrames[property]
+                ?: error("No frames for given target object and property.")) {
+
+            // Check whether this frame satisfies the current matcher.
+            if (curMatcher(update)) {
+
+                // If that was the last unsatisfied matcher, we're good here. 'Verify' all remaining
+                // frames and return without failing.
+                if (matchers.size == 0) {
+                    getAnimationUpdateFrames(animator).remove(property)
+                    return
+                }
+
+                frameTraceMessage.append("$update\t(satisfied matcher)\n")
+                curMatcher = matchers.pop() // Get the next matcher and keep going.
+            } else {
+                frameTraceMessage.append("${update}\n")
+            }
+        }
+
+        val readablePropertyName = PhysicsAnimator.getReadablePropertyName(property)
+        getAnimationUpdateFrames(animator).remove(property)
+
+        throw RuntimeException(
+                "Failed to verify animation frames for property $readablePropertyName: " +
+                        "Provided ${additionalUpdateMatchers.size + 1} matchers, " +
+                        "however ${matchers.size + 1} remained unsatisfied.\n\n" +
+                        "All frames:\n$frameTraceMessage")
+    }
+
+    /**
+     * Overload of [verifyAnimationUpdateFrames] that builds matchers for you, from given float
+     * values. For example, to verify that an animations passed from 0f to 50f to 100f back to 50f:
+     *
+     * verifyAnimationUpdateFrames(animator, TRANSLATION_X, 0f, 50f, 100f, 50f)
+     *
+     * This verifies that update frames were received with values of >= 0f, >= 50f, >= 100f, and
+     * <= 50f.
+     *
+     * The same caveats apply: short animations might not have enough frames to satisfy all of the
+     * matchers, and overly specific calls (such as 0f, 1f, 2f, 3f, etc. for an animation from
+     * x = 0f to x = 100f) might fail as the animation only had frames at 0f, 25f, 50f, 75f, and
+     * 100f. As with [verifyAnimationUpdateFrames], failures will print a helpful log of all frames
+     * so you can see what caused the test to fail.
+     */
+    fun <T : Any> verifyAnimationUpdateFrames(
+        animator: PhysicsAnimator<T>,
+        property: FloatPropertyCompat<in T>,
+        startValue: Float,
+        firstTargetValue: Float,
+        vararg additionalTargetValues: Float
+    ) {
+        val matchers = ArrayList<UpdateMatcher>()
+
+        val values = ArrayList<Float>().also {
+            it.add(firstTargetValue)
+            it.addAll(additionalTargetValues.toList())
+        }
+
+        var prevVal = startValue
+        for (value in values) {
+            if (value > prevVal) {
+                matchers.add { update -> update.value >= value }
+            } else {
+                matchers.add { update -> update.value <= value }
+            }
+
+            prevVal = value
+        }
+
+        verifyAnimationUpdateFrames(
+                animator, property, matchers[0], *matchers.drop(0).toTypedArray())
+    }
+
+    /**
+     * Returns all of the values that have ever been reported to update listeners, per property.
+     */
+    fun <T : Any> getAnimationUpdateFrames(animator: PhysicsAnimator<T>):
+            UpdateFramesPerProperty<T> {
+        return animatorTestHelpers[animator]?.getUpdates() as UpdateFramesPerProperty<T>
+    }
+
+    /**
+     * Clears animation frame updates from the given animator so they aren't used the next time its
+     * passed to [verifyAnimationUpdateFrames].
+     */
+    fun <T : Any> clearAnimationUpdateFrames(animator: PhysicsAnimator<T>) {
+        animatorTestHelpers[animator]?.clearUpdates()
+    }
+
+    private fun <T> getAnimationTestHelper(animator: PhysicsAnimator<T>): AnimatorTestHelper<T> {
+        return animatorTestHelpers[animator] as AnimatorTestHelper<T>
+    }
+
+    /**
+     * Helper class for testing an animator. This replaces the animator's start action with
+     * [startForTest] and adds test listeners to enable other test utility behaviors. We build one
+     * these for each Animator and keep them around so we can access the updates.
+     */
+    class AnimatorTestHelper<T> (private val animator: PhysicsAnimator<T>) {
+
+        /** All updates received for each property animation. */
+        private val allUpdates =
+                ArrayMap<FloatPropertyCompat<in T>, ArrayList<PhysicsAnimator.AnimationUpdate>>()
+
+        private val testEndListeners = ArrayList<PhysicsAnimator.EndListener<T>>()
+        private val testUpdateListeners = ArrayList<PhysicsAnimator.UpdateListener<T>>()
+
+        init {
+            animator.startAction = ::startForTest
+        }
+
+        internal fun addTestEndListener(listener: PhysicsAnimator.EndListener<T>) {
+            testEndListeners.add(listener)
+        }
+
+        internal fun addTestUpdateListener(listener: PhysicsAnimator.UpdateListener<T>) {
+            testUpdateListeners.add(listener)
+        }
+
+        internal fun getUpdates(): UpdateFramesPerProperty<T> {
+            return allUpdates
+        }
+
+        internal fun clearUpdates() {
+            allUpdates.clear()
+        }
+
+        private fun startForTest() {
+            // The testable animator needs to block the main thread until super.start() has been
+            // called, since callers expect .start() to be synchronous but we're posting it to a
+            // handler here. We may also continue blocking until all animations end, if
+            // startBlocksUntilAnimationsEnd = true.
+            val unblockLatch = CountDownLatch(if (startBlocksUntilAnimationsEnd) 2 else 1)
+
+            animationThreadHandler.post {
+                val animatedProperties = animator.getAnimatedProperties()
+
+                // Add an update listener that dispatches to any test update listeners added by
+                // tests.
+                animator.addUpdateListener(object : PhysicsAnimator.UpdateListener<T> {
+                    override fun onAnimationUpdateForProperty(
+                        target: T,
+                        values: ArrayMap<FloatPropertyCompat<in T>, PhysicsAnimator.AnimationUpdate>
+                    ) {
+                        for (listener in testUpdateListeners) {
+                            listener.onAnimationUpdateForProperty(target, values)
+                        }
+                    }
+                })
+
+                // Add an end listener that dispatches to any test end listeners added by tests, and
+                // unblocks the main thread if required.
+                animator.addEndListener(object : PhysicsAnimator.EndListener<T> {
+                    override fun onAnimationEnd(
+                        target: T,
+                        property: FloatPropertyCompat<in T>,
+                        canceled: Boolean,
+                        finalValue: Float,
+                        finalVelocity: Float,
+                        allRelevantPropertyAnimsEnded: Boolean
+                    ) {
+                        for (listener in testEndListeners) {
+                            listener.onAnimationEnd(
+                                    target, property, canceled, finalValue, finalVelocity,
+                                    allRelevantPropertyAnimsEnded)
+                        }
+
+                        if (allRelevantPropertyAnimsEnded) {
+                            testEndListeners.clear()
+                            testUpdateListeners.clear()
+
+                            if (startBlocksUntilAnimationsEnd) {
+                                unblockLatch.countDown()
+                            }
+                        }
+                    }
+                })
+
+                val updateListeners = ArrayList<PhysicsAnimator.UpdateListener<T>>().also {
+                    it.add(object : PhysicsAnimator.UpdateListener<T> {
+                        override fun onAnimationUpdateForProperty(
+                            target: T,
+                            values: ArrayMap<FloatPropertyCompat<in T>,
+                                             PhysicsAnimator.AnimationUpdate>
+                        ) {
+                            values.forEach { (property, value) ->
+                                allUpdates.getOrPut(property, { ArrayList() }).add(value)
+                            }
+                        }
+                    })
+                }
+
+                /**
+                 * Add an internal listener at the head of the list that captures update values
+                 * directly from DynamicAnimation. We use this to build a list of all updates so we
+                 * can verify that InternalListener dispatches to the real listeners properly.
+                 */
+                animator.internalListeners.add(0, animator.InternalListener(
+                        animatedProperties,
+                        updateListeners,
+                        ArrayList(),
+                        ArrayList()))
+
+                animator.startInternal()
+                unblockLatch.countDown()
+            }
+
+            unblockLatch.await(timeoutMs, TimeUnit.MILLISECONDS)
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 2ccecec..2bf855a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -261,7 +261,7 @@
 
     @Test
     public void testRemoveBubble_withDismissedNotif() {
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         assertTrue(mBubbleController.hasBubbles());
@@ -304,7 +304,7 @@
         assertFalse(mBubbleController.isStackExpanded());
 
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // We should have bubbles & their notifs should not be suppressed
@@ -334,8 +334,8 @@
     @Test
     public void testCollapseAfterChangingExpandedBubble() {
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
-        mEntryListener.onPendingEntryAdded(mRow2.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow2.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
         mBubbleController.updateBubble(mRow2.getEntry());
 
@@ -377,7 +377,7 @@
     @Test
     public void testExpansionRemovesShowInShadeAndDot() {
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // We should have bubbles & their notifs should not be suppressed
@@ -403,7 +403,7 @@
     @Test
     public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // We should have bubbles & their notifs should not be suppressed
@@ -439,8 +439,8 @@
     @Test
     public void testRemoveLastExpandedCollapses() {
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
-        mEntryListener.onPendingEntryAdded(mRow2.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow2.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
         mBubbleController.updateBubble(mRow2.getEntry());
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
@@ -483,7 +483,7 @@
                 Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */);
 
         // Add the auto expand bubble
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // Expansion shouldn't change
@@ -501,7 +501,7 @@
                 Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */);
 
         // Add the auto expand bubble
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // Expansion should change
@@ -519,7 +519,7 @@
                 Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
 
         // Add the suppress notif bubble
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // Notif should be suppressed because we were foreground
@@ -564,7 +564,7 @@
     public void testExpandStackAndSelectBubble_removedFirst() {
         final String key = mRow.getEntry().getKey();
 
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // Simulate notification cancellation.
@@ -576,7 +576,7 @@
 
     @Test
     public void testMarkNewNotificationAsShowInShade() {
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
         assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry().getKey()));
 
@@ -586,7 +586,7 @@
 
     @Test
     public void testAddNotif_notBubble() {
-        mEntryListener.onPendingEntryAdded(mNonBubbleNotifRow.getEntry());
+        mEntryListener.onNotificationAdded(mNonBubbleNotifRow.getEntry());
         mEntryListener.onPreEntryUpdated(mNonBubbleNotifRow.getEntry());
 
         verify(mBubbleStateChangeListener, never()).onHasBubblesChanged(anyBoolean());
@@ -631,7 +631,7 @@
 
     @Test
     public void testRemoveBubble_succeeds_appCancel() {
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         assertTrue(mBubbleController.hasBubbles());
@@ -646,7 +646,7 @@
 
     @Test
     public void removeBubble_fails_clearAll()  {
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         assertTrue(mBubbleController.hasBubbles());
@@ -669,7 +669,7 @@
 
     @Test
     public void removeBubble_fails_userDismissNotif() {
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         assertTrue(mBubbleController.hasBubbles());
@@ -692,7 +692,7 @@
 
     @Test
     public void removeBubble_succeeds_userDismissBubble_userDimissNotif() {
-        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onNotificationAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         assertTrue(mBubbleController.hasBubbles());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index f2665ef..775acdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -33,6 +33,7 @@
 
 import android.app.AlarmManager;
 import android.database.ContentObserver;
+import android.hardware.Sensor;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -80,6 +81,8 @@
     private TriggerSensor mTriggerSensor;
     @Mock
     private DozeLog mDozeLog;
+    @Mock
+    private Sensor mProximitySensor;
     private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
     private TestableLooper mTestableLooper;
     private DozeSensors mDozeSensors;
@@ -90,6 +93,7 @@
         mTestableLooper = TestableLooper.get(this);
         when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L);
         when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
+        when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(mProximitySensor);
         doAnswer(invocation -> {
             ((Runnable) invocation.getArgument(0)).run();
             return null;
@@ -98,6 +102,14 @@
     }
 
     @Test
+    public void testRegisterProx() {
+        // We should not register with the sensor manager initially.
+        verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt());
+        mDozeSensors.setProxListening(true);
+        verify(mSensorManager).registerListener(any(), any(Sensor.class), anyInt());
+    }
+
+    @Test
     public void testSensorDebounce() {
         mDozeSensors.setListening(true);
 
@@ -116,6 +128,7 @@
 
     @Test
     public void testSetListening_firstTrue_registerSettingsObserver() {
+        verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt());
         mDozeSensors.setListening(true);
 
         verify(mTriggerSensor).registerSettingsObserver(any(ContentObserver.class));
@@ -123,6 +136,7 @@
 
     @Test
     public void testSetListening_twiceTrue_onlyRegisterSettingsObserverOnce() {
+        verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt());
         mDozeSensors.setListening(true);
         mDozeSensors.setListening(true);
 
@@ -131,6 +145,7 @@
 
     @Test
     public void testSetPaused_doesntPause_sensors() {
+        verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt());
         mDozeSensors.setListening(true);
         verify(mTriggerSensor).setListening(eq(true));
 
@@ -147,8 +162,7 @@
 
         TestableDozeSensors() {
             super(getContext(), mAlarmManager, mSensorManager, mDozeParameters,
-                    mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback,
-                    mAlwaysOnDisplayPolicy, mDozeLog);
+                    mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog);
             for (TriggerSensor sensor : mSensors) {
                 if (sensor instanceof PluginSensor
                         && ((PluginSensor) sensor).mPluginSensor.getType()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index ba7b2e2..cef210c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -26,12 +26,14 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
+import android.app.Notification;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -198,11 +200,13 @@
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
 
+        final Notification notification = mock(Notification.class);
+        when(notification.isForegroundService()).thenReturn(true);
         NotificationEntry entry = new NotificationEntryBuilder()
                 .setImportance(IMPORTANCE_LOW)
+                .setNotification(notification)
                 .build();
         entry.setBucket(BUCKET_SILENT);
-        entry.setIsHighPriority(true);
         assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(entry));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
index d003b99..5310dd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -76,6 +76,7 @@
         mSmartActions = copyList(ranking.getSmartActions());
         mSmartReplies = copyList(ranking.getSmartReplies());
         mCanBubble = ranking.canBubble();
+        mIsVisuallyInterruptive = ranking.visuallyInterruptive();
     }
 
     public Ranking build() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryTest.java
new file mode 100644
index 0000000..721bbdc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.RankingBuilder;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class GroupEntryTest extends SysuiTestCase {
+    @Test
+    public void testIsHighPriority_addChild() {
+        // GIVEN a GroupEntry with a lowPrioritySummary and no children
+        final GroupEntry parentEntry = new GroupEntry("test_group_key");
+        final NotificationEntry lowPrioritySummary = createNotifEntry(false);
+        setSummary(parentEntry, lowPrioritySummary);
+        assertFalse(parentEntry.isHighPriority());
+
+        // WHEN we add a high priority child and invalidate derived members
+        addChild(parentEntry, createNotifEntry(true));
+        parentEntry.invalidateDerivedMembers();
+
+        // THEN the GroupEntry's priority is updated to high even though the summary is still low
+        // priority
+        assertTrue(parentEntry.isHighPriority());
+        assertFalse(lowPrioritySummary.isHighPriority());
+    }
+
+    @Test
+    public void testIsHighPriority_clearChildren() {
+        // GIVEN a GroupEntry with a lowPrioritySummary and high priority children
+        final GroupEntry parentEntry = new GroupEntry("test_group_key");
+        setSummary(parentEntry, createNotifEntry(false));
+        addChild(parentEntry, createNotifEntry(true));
+        addChild(parentEntry, createNotifEntry(true));
+        addChild(parentEntry, createNotifEntry(true));
+        assertTrue(parentEntry.isHighPriority());
+
+        // WHEN we clear the children and invalidate derived members
+        parentEntry.clearChildren();
+        parentEntry.invalidateDerivedMembers();
+
+        // THEN the parentEntry isn't high priority anymore
+        assertFalse(parentEntry.isHighPriority());
+    }
+
+    @Test
+    public void testIsHighPriority_summaryUpdated() {
+        // GIVEN a GroupEntry with a lowPrioritySummary and no children
+        final GroupEntry parentEntry = new GroupEntry("test_group_key");
+        final NotificationEntry lowPrioritySummary = createNotifEntry(false);
+        setSummary(parentEntry, lowPrioritySummary);
+        assertFalse(parentEntry.isHighPriority());
+
+        // WHEN the summary changes to high priority and invalidates its derived members
+        lowPrioritySummary.setRanking(
+                new RankingBuilder()
+                        .setKey(lowPrioritySummary.getKey())
+                        .setImportance(IMPORTANCE_HIGH)
+                        .build());
+        lowPrioritySummary.invalidateDerivedMembers();
+        assertTrue(lowPrioritySummary.isHighPriority());
+
+        // THEN the GroupEntry's priority is updated to high
+        assertTrue(parentEntry.isHighPriority());
+    }
+
+    @Test
+    public void testIsHighPriority_checkChildrenToCalculatePriority() {
+        // GIVEN:
+        // GroupEntry = parentEntry, summary = lowPrioritySummary
+        //      NotificationEntry = lowPriorityChild
+        //      NotificationEntry = highPriorityChild
+        final GroupEntry parentEntry = new GroupEntry("test_group_key");
+        setSummary(parentEntry, createNotifEntry(false));
+        addChild(parentEntry, createNotifEntry(false));
+        addChild(parentEntry, createNotifEntry(true));
+
+        // THEN the GroupEntry parentEntry is high priority since it has a high priority child
+        assertTrue(parentEntry.isHighPriority());
+    }
+
+    @Test
+    public void testIsHighPriority_childEntryRankingUpdated() {
+        // GIVEN:
+        // GroupEntry = parentEntry, summary = lowPrioritySummary
+        //      NotificationEntry = lowPriorityChild
+        final GroupEntry parentEntry = new GroupEntry("test_group_key");
+        final NotificationEntry lowPriorityChild = createNotifEntry(false);
+        setSummary(parentEntry, createNotifEntry(false));
+        addChild(parentEntry, lowPriorityChild);
+
+        // WHEN the child entry ranking changes to high priority and invalidates its derived members
+        lowPriorityChild.setRanking(
+                new RankingBuilder()
+                        .setKey(lowPriorityChild.getKey())
+                        .setImportance(IMPORTANCE_HIGH)
+                        .build());
+        lowPriorityChild.invalidateDerivedMembers();
+
+        // THEN the parent entry's high priority value is updated - but not the parent's summary
+        assertTrue(parentEntry.isHighPriority());
+        assertFalse(parentEntry.getSummary().isHighPriority());
+    }
+
+    private NotificationEntry createNotifEntry(boolean highPriority) {
+        return new NotificationEntryBuilder()
+                .setImportance(highPriority ? IMPORTANCE_HIGH : IMPORTANCE_MIN)
+                .build();
+    }
+
+    private void setSummary(GroupEntry parent, NotificationEntry summary) {
+        parent.setSummary(summary);
+        summary.setParent(parent);
+    }
+
+    private void addChild(GroupEntry parent, NotificationEntry child) {
+        parent.addChild(child);
+        child.setParent(parent);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 536aeb4..17d556d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -21,6 +21,8 @@
 import static android.app.Notification.CATEGORY_EVENT;
 import static android.app.Notification.CATEGORY_MESSAGE;
 import static android.app.Notification.CATEGORY_REMINDER;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 
 import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
@@ -91,6 +93,44 @@
     }
 
     @Test
+    public void testIsHighPriority_notificationUpdates() {
+        // GIVEN a notification with high importance
+        final NotificationEntry entryHigh = new NotificationEntryBuilder()
+                .setImportance(IMPORTANCE_HIGH)
+                .build();
+
+        // WHEN we get the value for the high priority entry, we're caching the high priority value
+        assertTrue(entryHigh.isHighPriority());
+
+        // WHEN we change the ranking and derived members (high priority) are invalidated
+        entryHigh.setRanking(
+                new RankingBuilder()
+                        .setKey(entryHigh.getKey())
+                        .setImportance(IMPORTANCE_MIN)
+                        .build());
+        entryHigh.invalidateDerivedMembers();
+
+        // THEN the priority is recalculated and is now low
+        assertFalse(entryHigh.isHighPriority());
+
+        // WHEN the sbn is updated to have messaging style (high priority characteristic)
+        //  AND the entry invalidates its derived members
+        final Notification notification =
+                new Notification.Builder(mContext, "test")
+                        .setStyle(new Notification.MessagingStyle(""))
+                        .build();
+        final StatusBarNotification sbn = entryHigh.getSbn();
+        entryHigh.setSbn(new StatusBarNotification(
+                sbn.getPackageName(), sbn.getPackageName(),
+                sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
+                notification, sbn.getUser(), sbn.getOverrideGroupKey(), 0));
+        entryHigh.invalidateDerivedMembers();
+
+        // THEN the priority is recalculated and is now high
+        assertTrue(entryHigh.isHighPriority());
+    }
+
+    @Test
     public void testIsExemptFromDndVisualSuppression_foreground() {
         mEntry.getSbn().getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index cda1538e..1764bef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -17,17 +17,12 @@
 package com.android.systemui.statusbar.notification.collection
 
 import android.app.Notification
-import android.app.NotificationChannel
 import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_HIGH
 import android.app.NotificationManager.IMPORTANCE_LOW
 import android.service.notification.NotificationListenerService.RankingMap
-import android.service.notification.StatusBarNotification
 import android.testing.AndroidTestingRunner
-
-import org.junit.runner.RunWith
-
 import androidx.test.filters.SmallTest
-
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.NotificationEntryBuilder
 import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking
@@ -42,12 +37,9 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import dagger.Lazy
 import junit.framework.Assert.assertEquals
-import junit.framework.Assert.assertFalse
-import junit.framework.Assert.assertTrue
-
 import org.junit.Before
 import org.junit.Test
-import org.mockito.Mockito.`when`
+import org.junit.runner.RunWith
 import org.mockito.Mockito.mock
 
 @SmallTest
@@ -76,106 +68,34 @@
     }
 
     @Test
-    fun testPeopleNotification_isHighPriority() {
-        val notification = Notification.Builder(mContext, "test")
-                .build()
-
-        val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
-                notification, mContext.user, "", 0)
-
-        `when`(personNotificationIdentifier.isPeopleNotification(sbn)).thenReturn(true)
-
-        val e = NotificationEntryBuilder()
-                .setNotification(notification)
-                .setSbn(sbn)
-                .build()
-
-        assertTrue(rankingManager.isHighPriority2(e))
-    }
-
-    @Test
-    fun lowForegroundHighPriority() {
-        val notification = mock(Notification::class.java)
-        `when`(notification.isForegroundService).thenReturn(true)
-
-        val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
-                notification, mContext.user, "", 0)
-
-        val e = NotificationEntryBuilder()
-                .setNotification(notification)
-                .setSbn(sbn)
-                .build()
-
-        modifyRanking(e)
-                .setImportance(IMPORTANCE_LOW)
-                .build()
-
-        assertTrue(rankingManager.isHighPriority2(e))
-    }
-
-    @Test
-    fun userChangeTrumpsHighPriorityCharacteristics() {
-        val notification = Notification.Builder(mContext, "test")
-                .setStyle(Notification.MessagingStyle(""))
-                .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
-                .build()
-
-        val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
-                notification, mContext.user, "", 0)
-
-        `when`(personNotificationIdentifier.isPeopleNotification(sbn)).thenReturn(true)
-
-        val channel = NotificationChannel("a", "a", IMPORTANCE_LOW)
-        channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE)
-
-        val e = NotificationEntryBuilder()
-                .setSbn(sbn)
-                .setChannel(channel)
-                .build()
-
-        assertFalse(rankingManager.isHighPriority2(e))
-    }
-
-    @Test
     fun testSort_highPriorityTrumpsNMSRank() {
         // NMS rank says A and then B. But A is not high priority and B is, so B should sort in
         // front
-        val aN = Notification.Builder(mContext, "test")
-                .setStyle(Notification.MessagingStyle(""))
-                .build()
         val a = NotificationEntryBuilder()
+                .setImportance(IMPORTANCE_LOW) // low priority
+                .setRank(1) // NMS says rank first
                 .setPkg("pkg")
                 .setOpPkg("pkg")
                 .setTag("tag")
-                .setNotification(aN)
+                .setNotification(
+                        Notification.Builder(mContext, "test")
+                                .build())
                 .setUser(mContext.getUser())
                 .setOverrideGroupKey("")
                 .build()
 
-        a.setIsHighPriority(false)
-
-        modifyRanking(a)
-                .setImportance(IMPORTANCE_LOW)
-                .setRank(1)
-                .build()
-
-        val bN = Notification.Builder(mContext, "test")
-                .setStyle(Notification.MessagingStyle(""))
-                .build()
         val b = NotificationEntryBuilder()
+                .setImportance(IMPORTANCE_HIGH) // high priority
+                .setRank(2) // NMS says rank second
                 .setPkg("pkg2")
                 .setOpPkg("pkg2")
                 .setTag("tag")
-                .setNotification(bN)
+                .setNotification(
+                        Notification.Builder(mContext, "test")
+                                .build())
                 .setUser(mContext.getUser())
                 .setOverrideGroupKey("")
                 .build()
-        b.setIsHighPriority(true)
-
-        modifyRanking(b)
-                .setImportance(IMPORTANCE_LOW)
-                .setRank(2)
-                .build()
 
         assertEquals(
                 listOf(b, a),
@@ -189,6 +109,8 @@
                 .setStyle(Notification.MessagingStyle(""))
                 .build()
         val a = NotificationEntryBuilder()
+                .setRank(1)
+                .setImportance(IMPORTANCE_HIGH)
                 .setPkg("pkg")
                 .setOpPkg("pkg")
                 .setTag("tag")
@@ -196,17 +118,13 @@
                 .setUser(mContext.getUser())
                 .setOverrideGroupKey("")
                 .build()
-        a.setIsHighPriority(false)
-
-        modifyRanking(a)
-                .setImportance(IMPORTANCE_LOW)
-                .setRank(1)
-                .build()
 
         val bN = Notification.Builder(mContext, "test")
                 .setStyle(Notification.MessagingStyle(""))
                 .build()
         val b = NotificationEntryBuilder()
+                .setRank(2)
+                .setImportance(IMPORTANCE_HIGH)
                 .setPkg("pkg2")
                 .setOpPkg("pkg2")
                 .setTag("tag")
@@ -214,12 +132,6 @@
                 .setUser(mContext.getUser())
                 .setOverrideGroupKey("")
                 .build()
-        b.setIsHighPriority(false)
-
-        modifyRanking(b)
-                .setImportance(IMPORTANCE_LOW)
-                .setRank(2)
-                .build()
 
         assertEquals(
                 listOf(a, b),
@@ -239,7 +151,7 @@
                 .setOverrideGroupKey("")
                 .build()
 
-        modifyRanking(e).setImportance(IMPORTANCE_DEFAULT) .build()
+        modifyRanking(e).setImportance(IMPORTANCE_DEFAULT).build()
 
         rankingManager.updateRanking(RankingMap(arrayOf(e.ranking)), listOf(e), "test")
         assertEquals(e.bucket, BUCKET_ALERTING)
@@ -281,11 +193,6 @@
         sectionsFeatureManager,
         peopleNotificationIdentifier
     ) {
-
-        fun isHighPriority2(e: NotificationEntry): Boolean {
-            return isHighPriority(e)
-        }
-
         fun applyTestRankingMap(r: RankingMap) {
             rankingMap = r
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProviderTest.java
new file mode 100644
index 0000000..11488a0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/IsHighPriorityProviderTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.provider;
+
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.Person;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class IsHighPriorityProviderTest extends SysuiTestCase {
+    private IsHighPriorityProvider mIsHighPriorityProvider;
+
+    @Before
+    public void setup() {
+        mIsHighPriorityProvider = new IsHighPriorityProvider();
+    }
+
+    @Test
+    public void testCache() {
+        // GIVEN a notification with high importance
+        final NotificationEntry entryHigh = new NotificationEntryBuilder()
+                .setImportance(IMPORTANCE_HIGH)
+                .build();
+
+        // GIVEN notification with min importance
+        final NotificationEntry entryMin = new NotificationEntryBuilder()
+                .setImportance(IMPORTANCE_MIN)
+                .build();
+
+        // WHEN we get the value for the high priority entry
+        assertTrue(mIsHighPriorityProvider.get(entryHigh));
+
+        // THEN the value is cached, so even when passed an entryMin, we still get high priority
+        assertTrue(mIsHighPriorityProvider.get(entryMin));
+
+        // UNTIL the provider is invalidated
+        mIsHighPriorityProvider.invalidate();
+
+        // THEN the priority is recalculated
+        assertFalse(mIsHighPriorityProvider.get(entryMin));
+    }
+
+    @Test
+    public void highImportance() {
+        // GIVEN notification has high importance
+        final NotificationEntry entry = new NotificationEntryBuilder()
+                .setImportance(IMPORTANCE_HIGH)
+                .build();
+
+        // THEN it has high priority
+        assertTrue(mIsHighPriorityProvider.get(entry));
+    }
+
+    @Test
+    public void peopleNotification() {
+        // GIVEN notification is low importance but has a person associated with it
+        final Notification notification = new Notification.Builder(mContext, "test")
+                .addPerson(
+                        new Person.Builder()
+                                .setName("name")
+                                .setKey("abc")
+                                .setUri("uri")
+                                .setBot(true)
+                                .build())
+                .build();
+
+        final NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .setImportance(IMPORTANCE_LOW)
+                .build();
+
+        // THEN it has high priority
+        assertTrue(mIsHighPriorityProvider.get(entry));
+    }
+
+    @Test
+    public void messagingStyle() {
+        // GIVEN notification is low importance but has messaging style
+        final Notification notification = new Notification.Builder(mContext, "test")
+                .setStyle(new Notification.MessagingStyle(""))
+                .build();
+
+        final NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .build();
+
+        // THEN it has high priority
+        assertTrue(mIsHighPriorityProvider.get(entry));
+    }
+
+    @Test
+    public void lowImportanceForeground() {
+        // GIVEN notification is low importance and is associated with a foreground service
+        final Notification notification = mock(Notification.class);
+        when(notification.isForegroundService()).thenReturn(true);
+
+        final NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .setImportance(IMPORTANCE_LOW)
+                .build();
+
+        // THEN it has high priority
+        assertTrue(mIsHighPriorityProvider.get(entry));
+    }
+
+    @Test
+    public void minImportanceForeground() {
+        // GIVEN notification is low importance and is associated with a foreground service
+        final Notification notification = mock(Notification.class);
+        when(notification.isForegroundService()).thenReturn(true);
+
+        final NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .setImportance(IMPORTANCE_MIN)
+                .build();
+
+        // THEN it does NOT have high priority
+        assertFalse(mIsHighPriorityProvider.get(entry));
+    }
+
+    @Test
+    public void userChangeTrumpsHighPriorityCharacteristics() {
+        // GIVEN notification has high priority characteristics but the user changed the importance
+        // to less than IMPORTANCE_DEFAULT (ie: IMPORTANCE_LOW or IMPORTANCE_MIN)
+        final Notification notification = new Notification.Builder(mContext, "test")
+                .addPerson(
+                        new Person.Builder()
+                                .setName("name")
+                                .setKey("abc")
+                                .setUri("uri")
+                                .setBot(true)
+                                .build())
+                .setStyle(new Notification.MessagingStyle(""))
+                .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+                .build();
+
+        final NotificationChannel channel = new NotificationChannel("a", "a",
+                IMPORTANCE_LOW);
+        channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+
+        final NotificationEntry entry = new NotificationEntryBuilder()
+                .setNotification(notification)
+                .setChannel(channel)
+                .build();
+
+        // THEN it does NOT have high priority
+        assertFalse(mIsHighPriorityProvider.get(entry));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 43d39a2..ccc9496 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -20,6 +20,7 @@
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 
 import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
@@ -383,15 +384,14 @@
         NotificationInfo notificationInfoView = mock(NotificationInfo.class);
         ExpandableNotificationRow row = spy(mHelper.createRow());
         row.setBlockingHelperShowing(true);
-        modifyRanking(row.getEntry())
+        final NotificationEntry entry = row.getEntry();
+        modifyRanking(entry)
                 .setUserSentiment(USER_SENTIMENT_NEGATIVE)
-                .setImportance(IMPORTANCE_DEFAULT)
+                .setImportance(IMPORTANCE_HIGH)
                 .build();
-        row.getEntry().setIsHighPriority(true);
-        when(row.getIsNonblockable()).thenReturn(false);
-        StatusBarNotification statusBarNotification = row.getEntry().getSbn();
-        NotificationEntry entry = row.getEntry();
 
+        when(row.getIsNonblockable()).thenReturn(false);
+        StatusBarNotification statusBarNotification = entry.getSbn();
         mGutsManager.initializeNotificationInfo(row, notificationInfoView);
 
         verify(notificationInfoView).bindNotification(
@@ -408,7 +408,7 @@
                 eq(false),
                 eq(false),
                 eq(true) /* isForBlockingHelper */,
-                eq(IMPORTANCE_DEFAULT),
+                eq(IMPORTANCE_HIGH),
                 eq(true) /* wasShownHighPriority */);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 4d6ff1f..008a349 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -760,6 +760,17 @@
     }
 
     @Test
+    public void testWillHideDockedWallpaper() {
+        mAlwaysOnEnabled = false;
+        when(mDockManager.isDocked()).thenReturn(true);
+        mScrimController.setWallpaperSupportsAmbientMode(true);
+
+        mScrimController.transitionTo(ScrimState.AOD);
+
+        verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
+    }
+
+    @Test
     public void testConservesExpansionOpacityAfterTransition() {
         mScrimController.transitionTo(ScrimState.UNLOCKED);
         mScrimController.setPanelExpansion(0.5f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt
new file mode 100644
index 0000000..a39fbc4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt
@@ -0,0 +1,436 @@
+package com.android.systemui.util.animation
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.util.ArrayMap
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.dynamicanimation.animation.SpringForce
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.animation.PhysicsAnimator.EndListener
+import com.android.systemui.util.animation.PhysicsAnimator.UpdateListener
+import com.android.systemui.util.animation.PhysicsAnimatorTestUtils.clearAnimationUpdateFrames
+import com.android.systemui.util.animation.PhysicsAnimatorTestUtils.getAnimationUpdateFrames
+import com.android.systemui.util.animation.PhysicsAnimatorTestUtils.verifyAnimationUpdateFrames
+import org.junit.After
+import org.junit.Assert
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.Mockito
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class PhysicsAnimatorTest : SysuiTestCase() {
+    private lateinit var viewGroup: ViewGroup
+    private lateinit var testView: View
+    private lateinit var testView2: View
+
+    private lateinit var animator: PhysicsAnimator<View>
+
+    private val springConfig = PhysicsAnimator.SpringConfig(
+            SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY)
+    private val flingConfig = PhysicsAnimator.FlingConfig(2f)
+
+    private lateinit var mockUpdateListener: UpdateListener<View>
+    private lateinit var mockEndListener: EndListener<View>
+    private lateinit var mockEndAction: Runnable
+
+    private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        mockUpdateListener = mock(UpdateListener::class.java) as UpdateListener<View>
+        mockEndListener = mock(EndListener::class.java) as EndListener<View>
+        mockEndAction = mock(Runnable::class.java)
+
+        viewGroup = FrameLayout(context)
+        testView = View(context)
+        testView2 = View(context)
+        viewGroup.addView(testView)
+        viewGroup.addView(testView2)
+
+        PhysicsAnimatorTestUtils.prepareForTest()
+
+        // Most of our tests involve checking the end state of animations, so we want calls that
+        // start animations to block the test thread until the animations have ended.
+        PhysicsAnimatorTestUtils.setAllAnimationsBlock(true)
+
+        animator = PhysicsAnimator.getInstance(testView)
+    }
+
+    @After
+    fun tearDown() {
+        PhysicsAnimatorTestUtils.tearDown()
+    }
+
+    @Test
+    fun testOneAnimatorPerView() {
+        assertEquals(animator, PhysicsAnimator.getInstance(testView))
+        assertEquals(PhysicsAnimator.getInstance(testView), PhysicsAnimator.getInstance(testView))
+        assertNotEquals(animator, PhysicsAnimator.getInstance(testView2))
+    }
+
+    @Test
+    fun testSpringOneProperty() {
+        animator
+                .spring(DynamicAnimation.TRANSLATION_X, 50f, springConfig)
+                .start()
+
+        assertEquals(testView.translationX, 50f, 1f)
+    }
+
+    @Test
+    fun testSpringMultipleProperties() {
+        animator
+                .spring(DynamicAnimation.TRANSLATION_X, 10f, springConfig)
+                .spring(DynamicAnimation.TRANSLATION_Y, 50f, springConfig)
+                .spring(DynamicAnimation.SCALE_Y, 1.1f, springConfig)
+                .start()
+
+        assertEquals(10f, testView.translationX, 1f)
+        assertEquals(50f, testView.translationY, 1f)
+        assertEquals(1.1f, testView.scaleY, 0.01f)
+    }
+
+    @Test
+    fun testFling() {
+        val startTime = System.currentTimeMillis()
+
+        animator
+                .fling(DynamicAnimation.TRANSLATION_X, 1000f /* startVelocity */, flingConfig)
+                .fling(DynamicAnimation.TRANSLATION_Y, 500f, flingConfig)
+                .start()
+
+        val elapsedTimeSeconds = (System.currentTimeMillis() - startTime) / 1000f
+
+        // If the fling worked, the view should be somewhere between its starting position and the
+        // and the theoretical no-friction maximum of startVelocity (in pixels per second)
+        // multiplied by elapsedTimeSeconds. We can't calculate an exact expected location for a
+        // fling, so this is close enough.
+        assertTrue(testView.translationX > 0f)
+        assertTrue(testView.translationX < 1000f * elapsedTimeSeconds)
+        assertTrue(testView.translationY > 0f)
+        assertTrue(testView.translationY < 500f * elapsedTimeSeconds)
+    }
+
+    @Test
+    @Throws(InterruptedException::class)
+    fun testEndListenersAndActions() {
+        PhysicsAnimatorTestUtils.setAllAnimationsBlock(false)
+        animator
+                .spring(DynamicAnimation.TRANSLATION_X, 10f, springConfig)
+                .spring(DynamicAnimation.TRANSLATION_Y, 500f, springConfig)
+                .addEndListener(mockEndListener)
+                .withEndActions(mockEndAction::run)
+                .start()
+
+        PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(animator, DynamicAnimation.TRANSLATION_X)
+
+        // Once TRANSLATION_X is done, the view should be at x = 10...
+        assertEquals(10f, testView.translationX, 1f)
+
+        // / ...TRANSLATION_Y should still be running...
+        assertTrue(animator.isPropertyAnimating(DynamicAnimation.TRANSLATION_Y))
+
+        // ...and our end listener should have been called with x = 10, velocity = 0, and allEnded =
+        // false since TRANSLATION_Y is still running.
+        verify(mockEndListener).onAnimationEnd(
+                testView,
+                DynamicAnimation.TRANSLATION_X,
+                canceled = false,
+                finalValue = 10f,
+                finalVelocity = 0f,
+                allRelevantPropertyAnimsEnded = false)
+        verifyNoMoreInteractions(mockEndListener)
+
+        // The end action should not have been run yet.
+        verify(mockEndAction, times(0)).run()
+
+        // Block until TRANSLATION_Y finishes.
+        PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(animator, DynamicAnimation.TRANSLATION_Y)
+
+        // The view should have been moved.
+        assertEquals(10f, testView.translationX, 1f)
+        assertEquals(500f, testView.translationY, 1f)
+
+        // The end listener should have been called, this time with TRANSLATION_Y, y = 50, and
+        // allEnded = true.
+        verify(mockEndListener).onAnimationEnd(
+                testView,
+                DynamicAnimation.TRANSLATION_Y,
+                canceled = false,
+                finalValue = 500f,
+                finalVelocity = 0f,
+                allRelevantPropertyAnimsEnded = true)
+        verifyNoMoreInteractions(mockEndListener)
+
+        // Now that all properties are done animating, the end action should have been called.
+        verify(mockEndAction, times(1)).run()
+    }
+
+    @Test
+    fun testUpdateListeners() {
+        animator
+                .spring(DynamicAnimation.TRANSLATION_X, 100f, springConfig)
+                .spring(DynamicAnimation.TRANSLATION_Y, 50f, springConfig)
+                .addUpdateListener(object : UpdateListener<View> {
+                    override fun onAnimationUpdateForProperty(
+                        target: View,
+                        values: UpdateMap<View>
+                    ) {
+                        mockUpdateListener.onAnimationUpdateForProperty(target, values)
+                    }
+                })
+                .start()
+
+        verifyUpdateListenerCalls(animator, mockUpdateListener)
+    }
+
+    @Test
+    fun testListenersNotCalledOnSubsequentAnimations() {
+        animator
+                .spring(DynamicAnimation.TRANSLATION_X, 10f, springConfig)
+                .addUpdateListener(mockUpdateListener)
+                .addEndListener(mockEndListener)
+                .withEndActions(mockEndAction::run)
+                .start()
+
+        verifyUpdateListenerCalls(animator, mockUpdateListener)
+        verify(mockEndListener, times(1)).onAnimationEnd(
+                eq(testView), eq(DynamicAnimation.TRANSLATION_X), eq(false), anyFloat(), anyFloat(),
+                eq(true))
+        verify(mockEndAction, times(1)).run()
+
+        animator
+                .spring(DynamicAnimation.TRANSLATION_X, 0f, springConfig)
+                .start()
+
+        // We didn't pass any of the listeners/actions to the subsequent animation, so they should
+        // never have been called.
+        verifyNoMoreInteractions(mockUpdateListener)
+        verifyNoMoreInteractions(mockEndListener)
+        verifyNoMoreInteractions(mockEndAction)
+    }
+
+    @Test
+    @Throws(InterruptedException::class)
+    fun testAnimationsUpdatedWhileInMotion() {
+        PhysicsAnimatorTestUtils.setAllAnimationsBlock(false)
+
+        // Spring towards x = 100f.
+        animator
+                .spring(
+                        DynamicAnimation.TRANSLATION_X,
+                        100f,
+                        springConfig)
+                .start()
+
+        // Block until it reaches x = 50f.
+        PhysicsAnimatorTestUtils.blockUntilFirstAnimationFrameWhereTrue(
+                animator) { view -> view.translationX > 50f }
+
+        // Translation X value at the time of reversing the animation to spring to x = 0f.
+        val reversalTranslationX = testView.translationX
+
+        // Spring back towards 0f.
+        animator
+                .spring(
+                        DynamicAnimation.TRANSLATION_X,
+                        0f,
+                        // Lower the stiffness to ensure the update listener receives at least one
+                        // update frame where the view has continued to move to the right.
+                        springConfig.apply { stiffness = SpringForce.STIFFNESS_LOW })
+                .start()
+
+        // Wait for TRANSLATION_X.
+        PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(animator, DynamicAnimation.TRANSLATION_X)
+
+        // Verify that the animation continued past the X value at the time of reversal, before
+        // springing back. This ensures the change in direction was not abrupt.
+        verifyAnimationUpdateFrames(
+                animator, DynamicAnimation.TRANSLATION_X,
+                { u -> u.value > reversalTranslationX },
+                { u -> u.value < reversalTranslationX })
+
+        // Verify that the view is where it should be.
+        assertEquals(0f, testView.translationX, 1f)
+    }
+
+    @Test
+    @Throws(InterruptedException::class)
+    fun testAnimationsUpdatedWhileInMotion_originalListenersStillCalled() {
+        PhysicsAnimatorTestUtils.setAllAnimationsBlock(false)
+
+        // Spring TRANSLATION_X to 100f, with an update and end listener provided.
+        animator
+                .spring(DynamicAnimation.TRANSLATION_X, 100f, springConfig)
+                .addUpdateListener(mockUpdateListener)
+                .addEndListener(mockEndListener)
+                .start()
+
+        // Wait until the animation is halfway there.
+        PhysicsAnimatorTestUtils.blockUntilFirstAnimationFrameWhereTrue(
+                animator) { view -> view.translationX > 50f }
+
+        // The end listener shouldn't have been called since the animation hasn't ended.
+        verifyNoMoreInteractions(mockEndListener)
+
+        // Make sure we called the update listener with appropriate values.
+        verifyAnimationUpdateFrames(animator, DynamicAnimation.TRANSLATION_X,
+                { u -> u.value > 0f },
+                { u -> u.value >= 50f })
+
+        // Mock a second end listener.
+        val secondEndListener = mock(EndListener::class.java) as EndListener<View>
+        val secondUpdateListener = mock(UpdateListener::class.java) as UpdateListener<View>
+
+        // Start a new animation that springs both TRANSLATION_X and TRANSLATION_Y, and provide it
+        // the second end listener. This new end listener should be called for the end of
+        // TRANSLATION_X and TRANSLATION_Y, with allEnded = true when both have ended.
+        animator
+                .spring(DynamicAnimation.TRANSLATION_X, 200f, springConfig)
+                .spring(DynamicAnimation.TRANSLATION_Y, 4000f, springConfig)
+                .addUpdateListener(secondUpdateListener)
+                .addEndListener(secondEndListener)
+                .start()
+
+        // Wait for TRANSLATION_X to end.
+        PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(animator, DynamicAnimation.TRANSLATION_X)
+
+        // The update listener provided to the initial animation call (the one that only animated
+        // TRANSLATION_X) should have been called with values on the way to x = 200f. This is
+        // because the second animation call updated the original TRANSLATION_X animation.
+        verifyAnimationUpdateFrames(
+                animator, DynamicAnimation.TRANSLATION_X,
+                { u -> u.value > 100f }, { u -> u.value >= 200f })
+
+        // The original end listener should also have been called, with allEnded = true since it was
+        // provided to an animator that animated only TRANSLATION_X.
+        verify(mockEndListener, times(1))
+                .onAnimationEnd(testView, DynamicAnimation.TRANSLATION_X, false, 200f, 0f, true)
+        verifyNoMoreInteractions(mockEndListener)
+
+        // The second end listener should have been called, but with allEnded = false since it was
+        // provided to an animator that animated both TRANSLATION_X and TRANSLATION_Y.
+        verify(secondEndListener, times(1))
+                .onAnimationEnd(testView, DynamicAnimation.TRANSLATION_X, false, 200f, 0f, false)
+        verifyNoMoreInteractions(secondEndListener)
+
+        PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(animator, DynamicAnimation.TRANSLATION_Y)
+
+        // The original end listener shouldn't receive any callbacks because it was not provided to
+        // an animator that animated TRANSLATION_Y.
+        verifyNoMoreInteractions(mockEndListener)
+
+        verify(secondEndListener, times(1))
+                .onAnimationEnd(testView, DynamicAnimation.TRANSLATION_Y, false, 4000f, 0f, true)
+        verifyNoMoreInteractions(secondEndListener)
+    }
+
+    @Test
+    fun testFlingRespectsMinMax() {
+        animator
+                .fling(DynamicAnimation.TRANSLATION_X,
+                        startVelocity = 1000f,
+                        friction = 1.1f,
+                        max = 10f)
+                .addEndListener(mockEndListener)
+                .start()
+
+        // Ensure that the view stopped at x = 10f, and the end listener was called once with that
+        // value.
+        assertEquals(10f, testView.translationX, 1f)
+        verify(mockEndListener, times(1))
+                .onAnimationEnd(
+                        eq(testView), eq(DynamicAnimation.TRANSLATION_X), eq(false), eq(10f),
+                        anyFloat(), eq(true))
+
+        animator
+                .fling(
+                        DynamicAnimation.TRANSLATION_X,
+                        startVelocity = -1000f,
+                        friction = 1.1f,
+                        min = -5f)
+                .addEndListener(mockEndListener)
+                .start()
+
+        // Ensure that the view stopped at x = -5f, and the end listener was called once with that
+        // value.
+        assertEquals(-5f, testView.translationX, 1f)
+        verify(mockEndListener, times(1))
+                .onAnimationEnd(
+                        eq(testView), eq(DynamicAnimation.TRANSLATION_X), eq(false), eq(-5f),
+                        anyFloat(), eq(true))
+    }
+
+    @Test
+    fun testExtensionProperty() {
+        testView
+                .physicsAnimator
+                .spring(DynamicAnimation.TRANSLATION_X, 200f)
+                .start()
+
+        assertEquals(200f, testView.translationX, 1f)
+    }
+
+    /**
+     * Verifies that the calls to the mock update listener match the animation update frames
+     * reported by the test internal listener, in order.
+     */
+    private fun <T : Any> verifyUpdateListenerCalls(
+        animator: PhysicsAnimator<T>,
+        mockUpdateListener: UpdateListener<T>
+    ) {
+        val updates = getAnimationUpdateFrames(animator)
+
+        for (invocation in Mockito.mockingDetails(mockUpdateListener).invocations) {
+
+            // Grab the update map of Property -> AnimationUpdate that was passed to the mock update
+            // listener.
+            val updateMap = invocation.arguments[1]
+                    as ArrayMap<FloatPropertyCompat<in T>, PhysicsAnimator.AnimationUpdate>
+
+            //
+            for ((property, update) in updateMap) {
+                val updatesForProperty = updates[property]!!
+
+                // This update should be the next one in the list for this property.
+                if (update != updatesForProperty[0]) {
+                    Assert.fail("The update listener was called with an unexpected value: $update.")
+                }
+
+                updatesForProperty.remove(update)
+            }
+
+            // Mark this invocation verified.
+            verify(mockUpdateListener).onAnimationUpdateForProperty(animator.target, updateMap)
+        }
+
+        verifyNoMoreInteractions(mockUpdateListener)
+
+        // Since we were removing values as matching invocations were found, there should no longer
+        // be any values remaining. If there are, it means the update listener wasn't notified when
+        // it should have been.
+        assertEquals(0,
+                updates.values.fold(0, { count, propertyUpdates -> count + propertyUpdates.size }))
+
+        clearAnimationUpdateFrames(animator)
+    }
+}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
deleted file mode 100644
index 3dfe59e..0000000
--- a/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
+++ /dev/null
@@ -1,640 +0,0 @@
-/*
- ** Copyright 2015, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- **     http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
-
-package com.android.server.accessibility.gestures;
-
-import android.accessibilityservice.AccessibilityGestureEvent;
-import android.accessibilityservice.AccessibilityService;
-import android.content.Context;
-import android.gesture.GesturePoint;
-import android.graphics.PointF;
-import android.util.Slog;
-import android.util.TypedValue;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-
-import java.util.ArrayList;
-
-/**
- * This class handles gesture detection for the Touch Explorer.  It collects
- * touch events and determines when they match a gesture, as well as when they
- * won't match a gesture.  These state changes are then surfaced to mListener.
- */
-class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListener {
-
-    private static final boolean DEBUG = false;
-
-    // Tag for logging received events.
-    private static final String LOG_TAG = "AccessibilityGestureDetector";
-
-    // Constants for sampling motion event points.
-    // We sample based on a minimum distance between points, primarily to improve accuracy by
-    // reducing noisy minor changes in direction.
-    private static final float MIN_INCHES_BETWEEN_SAMPLES = 0.1f;
-    private final float mMinPixelsBetweenSamplesX;
-    private final float mMinPixelsBetweenSamplesY;
-
-    // Constants for separating gesture segments
-    private static final float ANGLE_THRESHOLD = 0.0f;
-
-    // Constants for line segment directions
-    private static final int LEFT = 0;
-    private static final int RIGHT = 1;
-    private static final int UP = 2;
-    private static final int DOWN = 3;
-    private static final int[][] DIRECTIONS_TO_GESTURE_ID = {
-        {
-            AccessibilityService.GESTURE_SWIPE_LEFT,
-            AccessibilityService.GESTURE_SWIPE_LEFT_AND_RIGHT,
-            AccessibilityService.GESTURE_SWIPE_LEFT_AND_UP,
-            AccessibilityService.GESTURE_SWIPE_LEFT_AND_DOWN
-        },
-        {
-            AccessibilityService.GESTURE_SWIPE_RIGHT_AND_LEFT,
-            AccessibilityService.GESTURE_SWIPE_RIGHT,
-            AccessibilityService.GESTURE_SWIPE_RIGHT_AND_UP,
-            AccessibilityService.GESTURE_SWIPE_RIGHT_AND_DOWN
-        },
-        {
-            AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT,
-            AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT,
-            AccessibilityService.GESTURE_SWIPE_UP,
-            AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN
-        },
-        {
-            AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT,
-            AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT,
-            AccessibilityService.GESTURE_SWIPE_DOWN_AND_UP,
-            AccessibilityService.GESTURE_SWIPE_DOWN
-        }
-    };
-
-
-    /**
-     * Listener functions are called as a result of onMoveEvent().  The current
-     * MotionEvent in the context of these functions is the event passed into
-     * onMotionEvent.
-     */
-    public interface Listener {
-        /**
-         * Called when the user has performed a double tap and then held down
-         * the second tap.
-         *
-         * @param event The most recent MotionEvent received.
-         * @param policyFlags The policy flags of the most recent event.
-         */
-        void onDoubleTapAndHold(MotionEvent event, int policyFlags);
-
-        /**
-         * Called when the user lifts their finger on the second tap of a double
-         * tap.
-         *
-         * @param event The most recent MotionEvent received.
-         * @param policyFlags The policy flags of the most recent event.
-         *
-         * @return true if the event is consumed, else false
-         */
-        boolean onDoubleTap(MotionEvent event, int policyFlags);
-
-        /**
-         * Called when the system has decided the event stream is a gesture.
-         *
-         * @return true if the event is consumed, else false
-         */
-        boolean onGestureStarted();
-
-        /**
-         * Called when an event stream is recognized as a gesture.
-         *
-         * @param gestureEvent Information about the gesture.
-         *
-         * @return true if the event is consumed, else false
-         */
-        boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent);
-
-        /**
-         * Called when the system has decided an event stream doesn't match any
-         * known gesture.
-         *
-         * @param event The most recent MotionEvent received.
-         * @param policyFlags The policy flags of the most recent event.
-         *
-         * @return true if the event is consumed, else false
-         */
-        public boolean onGestureCancelled(MotionEvent event, int policyFlags);
-    }
-
-    private final Listener mListener;
-    private final Context mContext;  // Retained for on-demand construction of GestureDetector.
-    private final GestureDetector mGestureDetector;  // Double-tap detector.
-
-    // Indicates that a single tap has occurred.
-    private boolean mFirstTapDetected;
-
-    // Indicates that the down event of a double tap has occured.
-    private boolean mDoubleTapDetected;
-
-    // Indicates that motion events are being collected to match a gesture.
-    private boolean mRecognizingGesture;
-
-    // Indicates that we've collected enough data to be sure it could be a
-    // gesture.
-    private boolean mGestureStarted;
-
-    // Indicates that motion events from the second pointer are being checked
-    // for a double tap.
-    private boolean mSecondFingerDoubleTap;
-
-    // Tracks the most recent time where ACTION_POINTER_DOWN was sent for the
-    // second pointer.
-    private long mSecondPointerDownTime;
-
-    // Policy flags of the previous event.
-    private int mPolicyFlags;
-
-    // These values track the previous point that was saved to use for gesture
-    // detection.  They are only updated when the user moves more than the
-    // recognition threshold.
-    private float mPreviousGestureX;
-    private float mPreviousGestureY;
-
-    // These values track the previous point that was used to determine if there
-    // was a transition into or out of gesture detection.  They are updated when
-    // the user moves more than the detection threshold.
-    private float mBaseX;
-    private float mBaseY;
-    private long mBaseTime;
-
-    // This is the calculated movement threshold used track if the user is still
-    // moving their finger.
-    private final float mGestureDetectionThreshold;
-
-    // Buffer for storing points for gesture detection.
-    private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
-
-    // The minimal delta between moves to add a gesture point.
-    private static final int TOUCH_TOLERANCE = 3;
-
-    // The minimal score for accepting a predicted gesture.
-    private static final float MIN_PREDICTION_SCORE = 2.0f;
-
-    // Distance a finger must travel before we decide if it is a gesture or not.
-    private static final int GESTURE_CONFIRM_MM = 10;
-
-    // Time threshold used to determine if an interaction is a gesture or not.
-    // If the first movement of 1cm takes longer than this value, we assume it's
-    // a slow movement, and therefore not a gesture.
-    //
-    // This value was determined by measuring the time for the first 1cm
-    // movement when gesturing, and touch exploring.  Based on user testing,
-    // all gestures started with the initial movement taking less than 100ms.
-    // When touch exploring, the first movement almost always takes longer than
-    // 200ms.
-    private static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 150;
-
-    // Time threshold used to determine if a gesture should be cancelled.  If
-    // the finger takes more than this time to move 1cm, the ongoing gesture is
-    // cancelled.
-    private static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 300;
-
-    /**
-     * Construct the gesture detector for {@link TouchExplorer}.
-     *
-     * @see #AccessibilityGestureDetector(Context, Listener, GestureDetector)
-     */
-    AccessibilityGestureDetector(Context context, Listener listener) {
-        this(context, listener, null);
-    }
-
-    /**
-     * Construct the gesture detector for {@link TouchExplorer}.
-     *
-     * @param context A context handle for accessing resources.
-     * @param listener A listener to callback with gesture state or information.
-     * @param detector The gesture detector to handle touch event. If null the default one created
-     *                 in place, or for testing purpose.
-     */
-    AccessibilityGestureDetector(Context context, Listener listener, GestureDetector detector) {
-        mListener = listener;
-        mContext = context;
-
-        // Break the circular dependency between constructors and let the class to be testable
-        if (detector == null) {
-            mGestureDetector = new GestureDetector(context, this);
-        } else {
-            mGestureDetector = detector;
-        }
-        mGestureDetector.setOnDoubleTapListener(this);
-        mGestureDetectionThreshold = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1,
-                context.getResources().getDisplayMetrics()) * GESTURE_CONFIRM_MM;
-
-        // Calculate minimum gesture velocity
-        final float pixelsPerInchX = context.getResources().getDisplayMetrics().xdpi;
-        final float pixelsPerInchY = context.getResources().getDisplayMetrics().ydpi;
-        mMinPixelsBetweenSamplesX = MIN_INCHES_BETWEEN_SAMPLES * pixelsPerInchX;
-        mMinPixelsBetweenSamplesY = MIN_INCHES_BETWEEN_SAMPLES * pixelsPerInchY;
-    }
-
-    /**
-     * Handle a motion event.  If an action is completed, the appropriate
-     * callback on mListener is called, and the return value of the callback is
-     * passed to the caller.
-     *
-     * @param event The transformed motion event to be handled.
-     * @param rawEvent The raw motion event.  It's important that this be the raw
-     * event, before any transformations have been applied, so that measurements
-     * can be made in physical units.
-     * @param policyFlags Policy flags for the event.
-     *
-     * @return true if the event is consumed, else false
-     */
-    public boolean onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-        // The accessibility gesture detector is interested in the movements in physical space,
-        // so it uses the rawEvent to ignore magnification and other transformations.
-        final float x = rawEvent.getX();
-        final float y = rawEvent.getY();
-        final long time = rawEvent.getEventTime();
-
-        mPolicyFlags = policyFlags;
-        switch (rawEvent.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN:
-                mDoubleTapDetected = false;
-                mSecondFingerDoubleTap = false;
-                mRecognizingGesture = true;
-                mGestureStarted = false;
-                mPreviousGestureX = x;
-                mPreviousGestureY = y;
-                mStrokeBuffer.clear();
-                mStrokeBuffer.add(new GesturePoint(x, y, time));
-
-                mBaseX = x;
-                mBaseY = y;
-                mBaseTime = time;
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                if (mRecognizingGesture) {
-                    final float deltaX = mBaseX - x;
-                    final float deltaY = mBaseY - y;
-                    final double moveDelta = Math.hypot(deltaX, deltaY);
-                    if (moveDelta > mGestureDetectionThreshold) {
-                        // If the pointer has moved more than the threshold,
-                        // update the stored values.
-                        mBaseX = x;
-                        mBaseY = y;
-                        mBaseTime = time;
-
-                        // Since the pointer has moved, this is not a double
-                        // tap.
-                        mFirstTapDetected = false;
-                        mDoubleTapDetected = false;
-
-                        // If this hasn't been confirmed as a gesture yet, send
-                        // the event.
-                        if (!mGestureStarted) {
-                            mGestureStarted = true;
-                            return mListener.onGestureStarted();
-                        }
-                    } else if (!mFirstTapDetected) {
-                        // The finger may not move if they are double tapping.
-                        // In that case, we shouldn't cancel the gesture.
-                        final long timeDelta = time - mBaseTime;
-                        final long threshold = mGestureStarted ?
-                            CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS :
-                            CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS;
-
-                        // If the pointer hasn't moved for longer than the
-                        // timeout, cancel gesture detection.
-                        if (timeDelta > threshold) {
-                            cancelGesture();
-                            return mListener.onGestureCancelled(rawEvent, policyFlags);
-                        }
-                    }
-
-                    final float dX = Math.abs(x - mPreviousGestureX);
-                    final float dY = Math.abs(y - mPreviousGestureY);
-                    if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
-                        mPreviousGestureX = x;
-                        mPreviousGestureY = y;
-                        mStrokeBuffer.add(new GesturePoint(x, y, time));
-                    }
-                }
-                break;
-
-            case MotionEvent.ACTION_UP:
-                if (mDoubleTapDetected) {
-                    return finishDoubleTap(rawEvent, policyFlags);
-                }
-                if (mGestureStarted) {
-                    final float dX = Math.abs(x - mPreviousGestureX);
-                    final float dY = Math.abs(y - mPreviousGestureY);
-                    if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
-                        mStrokeBuffer.add(new GesturePoint(x, y, time));
-                    }
-                    return recognizeGesture(rawEvent, policyFlags);
-                }
-                break;
-
-            case MotionEvent.ACTION_POINTER_DOWN:
-                // Once a second finger is used, we're definitely not
-                // recognizing a gesture.
-                cancelGesture();
-
-                if (rawEvent.getPointerCount() == 2) {
-                    // If this was the second finger, attempt to recognize double
-                    // taps on it.
-                    mSecondFingerDoubleTap = true;
-                    mSecondPointerDownTime = time;
-                } else {
-                    // If there are more than two fingers down, stop watching
-                    // for a double tap.
-                    mSecondFingerDoubleTap = false;
-                }
-                break;
-
-            case MotionEvent.ACTION_POINTER_UP:
-                // If we're detecting taps on the second finger, see if we
-                // should finish the double tap.
-                if (mSecondFingerDoubleTap && mDoubleTapDetected) {
-                    return finishDoubleTap(rawEvent, policyFlags);
-                }
-                break;
-
-            case MotionEvent.ACTION_CANCEL:
-                clear();
-                break;
-        }
-
-        // If we're detecting taps on the second finger, map events from the
-        // finger to the first finger.
-        if (mSecondFingerDoubleTap) {
-            MotionEvent newEvent = mapSecondPointerToFirstPointer(rawEvent);
-            if (newEvent == null) {
-                return false;
-            }
-            boolean handled = mGestureDetector.onTouchEvent(newEvent);
-            newEvent.recycle();
-            return handled;
-        }
-
-        if (!mRecognizingGesture) {
-            return false;
-        }
-
-        // Pass the transformed event on to the standard gesture detector.
-        return mGestureDetector.onTouchEvent(event);
-    }
-
-    public void clear() {
-        mFirstTapDetected = false;
-        mDoubleTapDetected = false;
-        mSecondFingerDoubleTap = false;
-        mGestureStarted = false;
-        mGestureDetector.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_CANCEL,
-                0.0f, 0.0f, 0));
-        cancelGesture();
-    }
-
-
-    @Override
-    public void onLongPress(MotionEvent e) {
-        maybeSendLongPress(e, mPolicyFlags);
-    }
-
-    @Override
-    public boolean onSingleTapUp(MotionEvent event) {
-        mFirstTapDetected = true;
-        return false;
-    }
-
-    @Override
-    public boolean onSingleTapConfirmed(MotionEvent event) {
-        clear();
-        return false;
-    }
-
-    @Override
-    public boolean onDoubleTap(MotionEvent event) {
-        // The processing of the double tap is deferred until the finger is
-        // lifted, so that we can detect a long press on the second tap.
-        mDoubleTapDetected = true;
-        return false;
-    }
-
-    private void maybeSendLongPress(MotionEvent event, int policyFlags) {
-        if (!mDoubleTapDetected) {
-            return;
-        }
-
-        clear();
-
-        mListener.onDoubleTapAndHold(event, policyFlags);
-    }
-
-    private boolean finishDoubleTap(MotionEvent event, int policyFlags) {
-        clear();
-
-        return mListener.onDoubleTap(event, policyFlags);
-    }
-
-    private void cancelGesture() {
-        mRecognizingGesture = false;
-        mGestureStarted = false;
-        mStrokeBuffer.clear();
-    }
-
-    /**
-     * Looks at the sequence of motions in mStrokeBuffer, classifies the gesture, then calls
-     * Listener callbacks for success or failure.
-     *
-     * @param event The raw motion event to pass to the listener callbacks.
-     * @param policyFlags Policy flags for the event.
-     *
-     * @return true if the event is consumed, else false
-     */
-    private boolean recognizeGesture(MotionEvent event, int policyFlags) {
-        if (mStrokeBuffer.size() < 2) {
-            return mListener.onGestureCancelled(event, policyFlags);
-        }
-
-        // Look at mStrokeBuffer and extract 2 line segments, delimited by near-perpendicular
-        // direction change.
-        // Method: for each sampled motion event, check the angle of the most recent motion vector
-        // versus the preceding motion vector, and segment the line if the angle is about
-        // 90 degrees.
-
-        ArrayList<PointF> path = new ArrayList<>();
-        PointF lastDelimiter = new PointF(mStrokeBuffer.get(0).x, mStrokeBuffer.get(0).y);
-        path.add(lastDelimiter);
-
-        float dX = 0;  // Sum of unit vectors from last delimiter to each following point
-        float dY = 0;
-        int count = 0;  // Number of points since last delimiter
-        float length = 0;  // Vector length from delimiter to most recent point
-
-        PointF next = new PointF();
-        for (int i = 1; i < mStrokeBuffer.size(); ++i) {
-            next = new PointF(mStrokeBuffer.get(i).x, mStrokeBuffer.get(i).y);
-            if (count > 0) {
-                // Average of unit vectors from delimiter to following points
-                float currentDX = dX / count;
-                float currentDY = dY / count;
-
-                // newDelimiter is a possible new delimiter, based on a vector with length from
-                // the last delimiter to the previous point, but in the direction of the average
-                // unit vector from delimiter to previous points.
-                // Using the averaged vector has the effect of "squaring off the curve",
-                // creating a sharper angle between the last motion and the preceding motion from
-                // the delimiter. In turn, this sharper angle achieves the splitting threshold
-                // even in a gentle curve.
-                PointF newDelimiter = new PointF(length * currentDX + lastDelimiter.x,
-                    length * currentDY + lastDelimiter.y);
-
-                // Unit vector from newDelimiter to the most recent point
-                float nextDX = next.x - newDelimiter.x;
-                float nextDY = next.y - newDelimiter.y;
-                float nextLength = (float) Math.sqrt(nextDX * nextDX + nextDY * nextDY);
-                nextDX = nextDX / nextLength;
-                nextDY = nextDY / nextLength;
-
-                // Compare the initial motion direction to the most recent motion direction,
-                // and segment the line if direction has changed by about 90 degrees.
-                float dot = currentDX * nextDX + currentDY * nextDY;
-                if (dot < ANGLE_THRESHOLD) {
-                    path.add(newDelimiter);
-                    lastDelimiter = newDelimiter;
-                    dX = 0;
-                    dY = 0;
-                    count = 0;
-                }
-            }
-
-            // Vector from last delimiter to most recent point
-            float currentDX = next.x - lastDelimiter.x;
-            float currentDY = next.y - lastDelimiter.y;
-            length = (float) Math.sqrt(currentDX * currentDX + currentDY * currentDY);
-
-            // Increment sum of unit vectors from delimiter to each following point
-            count = count + 1;
-            dX = dX + currentDX / length;
-            dY = dY + currentDY / length;
-        }
-
-        path.add(next);
-        Slog.i(LOG_TAG, "path=" + path.toString());
-
-        // Classify line segments, and call Listener callbacks.
-        return recognizeGesturePath(event, policyFlags, path);
-    }
-
-    /**
-     * Classifies a pair of line segments, by direction.
-     * Calls Listener callbacks for success or failure.
-     *
-     * @param event The raw motion event to pass to the listener's onGestureCanceled method.
-     * @param policyFlags Policy flags for the event.
-     * @param path A sequence of motion line segments derived from motion points in mStrokeBuffer.
-     *
-     * @return true if the event is consumed, else false
-     */
-    private boolean recognizeGesturePath(MotionEvent event, int policyFlags,
-            ArrayList<PointF> path) {
-
-        final int displayId = event.getDisplayId();
-        if (path.size() == 2) {
-            PointF start = path.get(0);
-            PointF end = path.get(1);
-
-            float dX = end.x - start.x;
-            float dY = end.y - start.y;
-            int direction = toDirection(dX, dY);
-            switch (direction) {
-                case LEFT:
-                    return mListener.onGestureCompleted(
-                            new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_LEFT,
-                                    displayId));
-                case RIGHT:
-                    return mListener.onGestureCompleted(
-                            new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_RIGHT,
-                                    displayId));
-                case UP:
-                    return mListener.onGestureCompleted(
-                            new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_UP,
-                                    displayId));
-                case DOWN:
-                    return mListener.onGestureCompleted(
-                            new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_DOWN,
-                                    displayId));
-                default:
-                    // Do nothing.
-            }
-
-        } else if (path.size() == 3) {
-            PointF start = path.get(0);
-            PointF mid = path.get(1);
-            PointF end = path.get(2);
-
-            float dX0 = mid.x - start.x;
-            float dY0 = mid.y - start.y;
-
-            float dX1 = end.x - mid.x;
-            float dY1 = end.y - mid.y;
-
-            int segmentDirection0 = toDirection(dX0, dY0);
-            int segmentDirection1 = toDirection(dX1, dY1);
-            int gestureId = DIRECTIONS_TO_GESTURE_ID[segmentDirection0][segmentDirection1];
-            return mListener.onGestureCompleted(
-                    new AccessibilityGestureEvent(gestureId, displayId));
-        }
-        // else if (path.size() < 2 || 3 < path.size()) then no gesture recognized.
-        return mListener.onGestureCancelled(event, policyFlags);
-    }
-
-    /** Maps a vector to a dominant direction in set {LEFT, RIGHT, UP, DOWN}. */
-    private static int toDirection(float dX, float dY) {
-        if (Math.abs(dX) > Math.abs(dY)) {
-            // Horizontal
-            return (dX < 0) ? LEFT : RIGHT;
-        } else {
-            // Vertical
-            return (dY < 0) ? UP : DOWN;
-        }
-    }
-
-    private MotionEvent mapSecondPointerToFirstPointer(MotionEvent event) {
-        // Only map basic events when two fingers are down.
-        if (event.getPointerCount() != 2 ||
-                (event.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN &&
-                 event.getActionMasked() != MotionEvent.ACTION_POINTER_UP &&
-                 event.getActionMasked() != MotionEvent.ACTION_MOVE)) {
-            return null;
-        }
-
-        int action = event.getActionMasked();
-
-        if (action == MotionEvent.ACTION_POINTER_DOWN) {
-            action = MotionEvent.ACTION_DOWN;
-        } else if (action == MotionEvent.ACTION_POINTER_UP) {
-            action = MotionEvent.ACTION_UP;
-        }
-
-        // Map the information from the second pointer to the first.
-        return MotionEvent.obtain(mSecondPointerDownTime, event.getEventTime(), action,
-                event.getX(1), event.getY(1), event.getPressure(1), event.getSize(1),
-                event.getMetaState(), event.getXPrecision(), event.getYPrecision(),
-                event.getDeviceId(), event.getEdgeFlags());
-    }
-}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
new file mode 100644
index 0000000..9b7adc8
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -0,0 +1,232 @@
+/*
+ * 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.accessibility.gestures;
+
+import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_UP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT_AND_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT_AND_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT_AND_UP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT_AND_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT_AND_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT_AND_UP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT;
+
+import static com.android.server.accessibility.gestures.Swipe.DOWN;
+import static com.android.server.accessibility.gestures.Swipe.LEFT;
+import static com.android.server.accessibility.gestures.Swipe.RIGHT;
+import static com.android.server.accessibility.gestures.Swipe.UP;
+import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
+
+import android.accessibilityservice.AccessibilityGestureEvent;
+import android.content.Context;
+import android.os.Handler;
+import android.util.Slog;
+import android.view.MotionEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class coordinates a series of individual gesture matchers to serve as a unified gesture
+ * detector. Gesture matchers are tied to a single gesture. It calls listener callback functions
+ * when a gesture starts or completes.
+ */
+class GestureManifold implements GestureMatcher.StateChangeListener {
+
+    private static final String LOG_TAG = "GestureManifold";
+
+    private final List<GestureMatcher> mGestures = new ArrayList<>();
+    private final Context mContext;
+    // Handler for performing asynchronous operations.
+    private final Handler mHandler;
+    // Listener to be notified of gesture start and end.
+    private Listener mListener;
+    // Shared state information.
+    private TouchState mState;
+
+    GestureManifold(Context context, Listener listener, TouchState state) {
+        mContext = context;
+        mHandler = new Handler(context.getMainLooper());
+        mListener = listener;
+        mState = state;
+        // Set up gestures.
+        // Start with double tap.
+        mGestures.add(new MultiTap(context, 2, GESTURE_DOUBLE_TAP, this));
+        mGestures.add(new MultiTapAndHold(context, 2, GESTURE_DOUBLE_TAP_AND_HOLD, this));
+        // One-direction swipes.
+        mGestures.add(new Swipe(context, RIGHT, GESTURE_SWIPE_RIGHT, this));
+        mGestures.add(new Swipe(context, LEFT, GESTURE_SWIPE_LEFT, this));
+        mGestures.add(new Swipe(context, UP, GESTURE_SWIPE_UP, this));
+        mGestures.add(new Swipe(context, DOWN, GESTURE_SWIPE_DOWN, this));
+        // Two-direction swipes.
+        mGestures.add(new Swipe(context, LEFT, RIGHT, GESTURE_SWIPE_LEFT_AND_RIGHT, this));
+        mGestures.add(new Swipe(context, LEFT, UP, GESTURE_SWIPE_LEFT_AND_UP, this));
+        mGestures.add(new Swipe(context, LEFT, DOWN, GESTURE_SWIPE_LEFT_AND_DOWN, this));
+        mGestures.add(new Swipe(context, RIGHT, UP, GESTURE_SWIPE_RIGHT_AND_UP, this));
+        mGestures.add(new Swipe(context, RIGHT, DOWN, GESTURE_SWIPE_RIGHT_AND_DOWN, this));
+        mGestures.add(new Swipe(context, RIGHT, LEFT, GESTURE_SWIPE_RIGHT_AND_LEFT, this));
+        mGestures.add(new Swipe(context, DOWN, UP, GESTURE_SWIPE_DOWN_AND_UP, this));
+        mGestures.add(new Swipe(context, DOWN, LEFT, GESTURE_SWIPE_DOWN_AND_LEFT, this));
+        mGestures.add(new Swipe(context, DOWN, RIGHT, GESTURE_SWIPE_DOWN_AND_RIGHT, this));
+        mGestures.add(new Swipe(context, UP, DOWN, GESTURE_SWIPE_UP_AND_DOWN, this));
+        mGestures.add(new Swipe(context, UP, LEFT, GESTURE_SWIPE_UP_AND_LEFT, this));
+        mGestures.add(new Swipe(context, UP, RIGHT, GESTURE_SWIPE_UP_AND_RIGHT, this));
+    }
+
+    /**
+     * Processes a motion event.
+     *
+     * @param event The event as received from the previous entry in the event stream.
+     * @param rawEvent The event without any transformations e.g. magnification.
+     * @param policyFlags
+     * @return True if the event has been appropriately handled by the gesture manifold and related
+     *     callback functions, false if it should be handled further by the calling function.
+     */
+    boolean onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (mState.isClear()) {
+            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                // Sanity safeguard: if touch state is clear, then matchers should always be clear
+                // before processing the next down event.
+                clear();
+            } else {
+                // If for some reason other events come through while in the clear state they could
+                // compromise the state of particular matchers, so we just ignore them.
+                return false;
+            }
+        }
+        for (GestureMatcher matcher : mGestures) {
+            if (matcher.getState() != GestureMatcher.STATE_GESTURE_CANCELED) {
+                if (DEBUG) {
+                    Slog.d(LOG_TAG, matcher.toString());
+                }
+                matcher.onMotionEvent(event, rawEvent, policyFlags);
+                if (DEBUG) {
+                    Slog.d(LOG_TAG, matcher.toString());
+                }
+                if (matcher.getState() == GestureMatcher.STATE_GESTURE_COMPLETED) {
+                    // Here we just clear and return. The actual gesture dispatch is done in
+                    // onStateChanged().
+                    clear();
+                    // No need to process this event any further.
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public void clear() {
+        for (GestureMatcher matcher : mGestures) {
+            matcher.clear();
+        }
+    }
+
+    /**
+     * Listener that receives notifications of the state of the gesture detector. Listener functions
+     * are called as a result of onMotionEvent(). The current MotionEvent in the context of these
+     * functions is the event passed into onMotionEvent.
+     */
+    public interface Listener {
+        /**
+         * Called when the user has performed a double tap and then held down the second tap.
+         */
+        void onDoubleTapAndHold();
+
+        /**
+         * Called when the user lifts their finger on the second tap of a double tap.
+         * @return true if the event is consumed, else false
+         */
+        boolean onDoubleTap();
+
+        /**
+         * Called when the system has decided the event stream is a gesture.
+         *
+         * @return true if the event is consumed, else false
+         */
+        boolean onGestureStarted();
+
+        /**
+         * Called when an event stream is recognized as a gesture.
+         *
+         * @param gestureEvent Information about the gesture.
+         * @return true if the event is consumed, else false
+         */
+        boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent);
+
+        /**
+         * Called when the system has decided an event stream doesn't match any known gesture.
+         *
+         * @param event The most recent MotionEvent received.
+         * @param policyFlags The policy flags of the most recent event.
+         * @return true if the event is consumed, else false
+         */
+        boolean onGestureCancelled(MotionEvent event, MotionEvent rawEvent, int policyFlags);
+    }
+
+    @Override
+    public void onStateChanged(
+            int gestureId, int state, MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (state == GestureMatcher.STATE_GESTURE_STARTED && !mState.isGestureDetecting()) {
+            mListener.onGestureStarted();
+        } else if (state == GestureMatcher.STATE_GESTURE_COMPLETED) {
+            onGestureCompleted(gestureId);
+        } else if (state == GestureMatcher.STATE_GESTURE_CANCELED && mState.isGestureDetecting()) {
+            // We only want to call the cancelation callback if there are no other pending
+            // detectors.
+            for (GestureMatcher matcher : mGestures) {
+                if (matcher.getState() == GestureMatcher.STATE_GESTURE_STARTED) {
+                    return;
+                }
+            }
+            if (DEBUG) {
+                Slog.d(LOG_TAG, "Cancelling.");
+            }
+            mListener.onGestureCancelled(event, rawEvent, policyFlags);
+        }
+    }
+
+    private void onGestureCompleted(int gestureId) {
+        MotionEvent event = mState.getLastReceivedEvent();
+        // Note that gestures that complete immediately call clear() from onMotionEvent.
+        // Gestures that complete on a delay call clear() here.
+        switch (gestureId) {
+            case GESTURE_DOUBLE_TAP:
+                mListener.onDoubleTap();
+                clear();
+                break;
+            case GESTURE_DOUBLE_TAP_AND_HOLD:
+                mListener.onDoubleTapAndHold();
+                clear();
+                break;
+            default:
+                AccessibilityGestureEvent gestureEvent =
+                        new AccessibilityGestureEvent(gestureId, event.getDisplayId());
+                mListener.onGestureCompleted(gestureEvent);
+                break;
+        }
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java
new file mode 100644
index 0000000..0b30ff57
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java
@@ -0,0 +1,371 @@
+/*
+ * 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.accessibility.gestures;
+
+import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
+
+import android.annotation.IntDef;
+import android.os.Handler;
+import android.util.Slog;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+/**
+ * This class describes a common base for gesture matchers. A gesture matcher checks a series of
+ * motion events against a single gesture. Coordinating the individual gesture matchers is done by
+ * the GestureManifold. To create a new Gesture, extend this class and override the onDown, onMove,
+ * onUp, etc methods as necessary. If you don't override a method your matcher will do nothing in
+ * response to that type of event. Finally, be sure to give your gesture a name by overriding
+ * getGestureName().
+ */
+abstract class GestureMatcher {
+    // Potential states for this individual gesture matcher.
+    // In STATE_CLEAR, this matcher is accepting new motion events but has not formally signaled
+    // that there is enough data to judge that a gesture has started.
+    static final int STATE_CLEAR = 0;
+    // In STATE_GESTURE_STARTED, this matcher continues to accept motion events and it has signaled
+    // to the gesture manifold that what looks like the specified gesture has started.
+    static final int STATE_GESTURE_STARTED = 1;
+    // In STATE_GESTURE_COMPLETED, this matcher has successfully matched the specified gesture. and
+    // will not accept motion events until it is cleared.
+    static final int STATE_GESTURE_COMPLETED = 2;
+    // In STATE_GESTURE_CANCELED, this matcher will not accept new motion events because it is
+    // impossible that this set of motion events will match the specified gesture.
+    static final int STATE_GESTURE_CANCELED = 3;
+
+    @IntDef({STATE_CLEAR, STATE_GESTURE_STARTED, STATE_GESTURE_COMPLETED, STATE_GESTURE_CANCELED})
+    public @interface State {}
+
+    @State private int mState = STATE_CLEAR;
+    // The id number of the gesture that gets passed to accessibility services.
+    private final int mGestureId;
+    // handler for asynchronous operations like timeouts
+    private final Handler mHandler;
+
+    private final StateChangeListener mListener;
+
+    // Use this to transition to new states after a delay.
+    // e.g. cancel or complete after some timeout.
+    // Convenience functions for tapTimeout and doubleTapTimeout are already defined here.
+    protected final DelayedTransition mDelayedTransition;
+
+    GestureMatcher(int gestureId, Handler handler, StateChangeListener listener) {
+        mGestureId = gestureId;
+        mHandler = handler;
+        mDelayedTransition = new DelayedTransition();
+        mListener = listener;
+    }
+
+    /**
+     * Resets all state information for this matcher. Subclasses that include their own state
+     * information should override this method to reset their own state information and call
+     * super.clear().
+     */
+    protected void clear() {
+        mState = STATE_CLEAR;
+        cancelPendingTransitions();
+    }
+
+    public int getState() {
+        return mState;
+    }
+
+    /**
+     * Transitions to a new state and notifies any listeners. Note that any pending transitions are
+     * canceled.
+     */
+    private void setState(
+            @State int state, MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        mState = state;
+        cancelPendingTransitions();
+        mListener.onStateChanged(mGestureId, mState, event, rawEvent, policyFlags);
+    }
+
+    /** Indicates that there is evidence to suggest that this gesture has started. */
+    protected final void startGesture(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        setState(STATE_GESTURE_STARTED, event, rawEvent, policyFlags);
+    }
+
+    /** Indicates this stream of motion events can no longer match this gesture. */
+    protected final void cancelGesture(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        setState(STATE_GESTURE_CANCELED, event, rawEvent, policyFlags);
+    }
+
+    /** Indicates this gesture is completed. */
+    protected final void completeGesture(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        setState(STATE_GESTURE_COMPLETED, event, rawEvent, policyFlags);
+    }
+
+    public int getGestureId() {
+        return mGestureId;
+    }
+
+    /**
+     * Process a motion event and attempt to match it to this gesture.
+     *
+     * @param event the event as passed in from the event stream.
+     * @param rawEvent the original un-modified event. Useful for calculating movements in physical
+     *     space.
+     * @param policyFlags the policy flags as passed in from the event stream.
+     * @return the state of this matcher.
+     */
+    public final int onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (mState == STATE_GESTURE_CANCELED || mState == STATE_GESTURE_COMPLETED) {
+            return mState;
+        }
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                onDown(event, rawEvent, policyFlags);
+                break;
+            case MotionEvent.ACTION_POINTER_DOWN:
+                onPointerDown(event, rawEvent, policyFlags);
+                break;
+            case MotionEvent.ACTION_MOVE:
+                onMove(event, rawEvent, policyFlags);
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                onPointerUp(event, rawEvent, policyFlags);
+                break;
+            case MotionEvent.ACTION_UP:
+                onUp(event, rawEvent, policyFlags);
+                break;
+            default:
+                // Cancel because of invalid event.
+                setState(STATE_GESTURE_CANCELED, event, rawEvent, policyFlags);
+                break;
+        }
+        return mState;
+    }
+
+    /**
+     * Matchers override this method to respond to ACTION_DOWN events. ACTION_DOWN events indicate
+     * the first finger has touched the screen. If not overridden the default response is to do
+     * nothing.
+     */
+    protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {}
+
+    /**
+     * Matchers override this method to respond to ACTION_POINTER_DOWN events. ACTION_POINTER_DOWN
+     * indicates that more than one finger has touched the screen. If not overridden the default
+     * response is to do nothing.
+     *
+     * @param event the event as passed in from the event stream.
+     * @param rawEvent the original un-modified event. Useful for calculating movements in physical
+     *     space.
+     * @param policyFlags the policy flags as passed in from the event stream.
+     */
+    protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {}
+
+    /**
+     * Matchers override this method to respond to ACTION_MOVE events. ACTION_MOVE indicates that
+     * one or fingers has moved. If not overridden the default response is to do nothing.
+     *
+     * @param event the event as passed in from the event stream.
+     * @param rawEvent the original un-modified event. Useful for calculating movements in physical
+     *     space.
+     * @param policyFlags the policy flags as passed in from the event stream.
+     */
+    protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {}
+
+    /**
+     * Matchers override this method to respond to ACTION_POINTER_UP events. ACTION_POINTER_UP
+     * indicates that a finger has lifted from the screen but at least one finger continues to touch
+     * the screen. If not overridden the default response is to do nothing.
+     *
+     * @param event the event as passed in from the event stream.
+     * @param rawEvent the original un-modified event. Useful for calculating movements in physical
+     *     space.
+     * @param policyFlags the policy flags as passed in from the event stream.
+     */
+    protected void onPointerUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {}
+
+    /**
+     * Matchers override this method to respond to ACTION_UP events. ACTION_UP indicates that there
+     * are no more fingers touching the screen. If not overridden the default response is to do
+     * nothing.
+     *
+     * @param event the event as passed in from the event stream.
+     * @param rawEvent the original un-modified event. Useful for calculating movements in physical
+     *     space.
+     * @param policyFlags the policy flags as passed in from the event stream.
+     */
+    protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {}
+
+    /** Cancels this matcher after the tap timeout. Any pending state transitions are removed. */
+    protected void cancelAfterTapTimeout(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        cancelAfter(ViewConfiguration.getTapTimeout(), event, rawEvent, policyFlags);
+    }
+
+    /** Cancels this matcher after the double tap timeout. Any pending cancelations are removed. */
+    protected final void cancelAfterDoubleTapTimeout(
+            MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        cancelAfter(ViewConfiguration.getDoubleTapTimeout(), event, rawEvent, policyFlags);
+    }
+
+    /**
+     * Cancels this matcher after the specified timeout. Any pending cancelations are removed. Used
+     * to prevent this matcher from accepting motion events until it is cleared.
+     */
+    protected final void cancelAfter(
+            long timeout, MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        mDelayedTransition.cancel();
+        mDelayedTransition.post(STATE_GESTURE_CANCELED, timeout, event, rawEvent, policyFlags);
+    }
+
+    /** Cancels any delayed transitions between states scheduled for this matcher. */
+    protected final void cancelPendingTransitions() {
+        mDelayedTransition.cancel();
+    }
+
+    /**
+     * Signals that this gesture has been completed after the tap timeout has expired. Used to
+     * ensure that there is no conflict with another gesture or for gestures that explicitly require
+     * a hold.
+     */
+    protected final void completeAfterLongPressTimeout(
+            MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        completeAfter(ViewConfiguration.getLongPressTimeout(), event, rawEvent, policyFlags);
+    }
+
+    /**
+     * Signals that this gesture has been completed after the tap timeout has expired. Used to
+     * ensure that there is no conflict with another gesture or for gestures that explicitly require
+     * a hold.
+     */
+    protected final void completeAfterTapTimeout(
+            MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        completeAfter(ViewConfiguration.getTapTimeout(), event, rawEvent, policyFlags);
+    }
+
+    /**
+     * Signals that this gesture has been completed after the specified timeout has expired. Used to
+     * ensure that there is no conflict with another gesture or for gestures that explicitly require
+     * a hold.
+     */
+    protected final void completeAfter(
+            long timeout, MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        mDelayedTransition.cancel();
+        mDelayedTransition.post(STATE_GESTURE_COMPLETED, timeout, event, rawEvent, policyFlags);
+    }
+
+    /**
+     * Signals that this gesture has been completed after the double-tap timeout has expired. Used
+     * to ensure that there is no conflict with another gesture or for gestures that explicitly
+     * require a hold.
+     */
+    protected final void completeAfterDoubleTapTimeout(
+            MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        completeAfter(ViewConfiguration.getDoubleTapTimeout(), event, rawEvent, policyFlags);
+    }
+
+    public static String getStateSymbolicName(@State int state) {
+        switch (state) {
+            case STATE_CLEAR:
+                return "STATE_CLEAR";
+            case STATE_GESTURE_STARTED:
+                return "STATE_GESTURE_STARTED";
+            case STATE_GESTURE_COMPLETED:
+                return "STATE_GESTURE_COMPLETED";
+            case STATE_GESTURE_CANCELED:
+                return "STATE_GESTURE_CANCELED";
+            default:
+                return "Unknown state: " + state;
+        }
+    }
+
+    /**
+     * Returns a readable name for this matcher that can be displayed to the user and in system
+     * logs.
+     */
+    abstract String getGestureName();
+
+    /**
+     * Returns a String representation of this matcher. Each matcher can override this method to add
+     * extra state information to the string representation.
+     */
+    public String toString() {
+        return getGestureName() + ":" + getStateSymbolicName(mState);
+    }
+
+    /** This class allows matchers to transition between states on a delay. */
+    protected final class DelayedTransition implements Runnable {
+
+        private static final String LOG_TAG = "GestureMatcher.DelayedTransition";
+        int mTargetState;
+        MotionEvent mEvent;
+        MotionEvent mRawEvent;
+        int mPolicyFlags;
+
+        public void cancel() {
+            // Avoid meaningless debug messages.
+            if (DEBUG && isPending()) {
+                Slog.d(
+                        LOG_TAG,
+                        getGestureName()
+                                + ": canceling delayed transition to "
+                                + getStateSymbolicName(mTargetState));
+            }
+            mHandler.removeCallbacks(this);
+        }
+
+        public void post(
+                int state, long delay, MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+            mTargetState = state;
+            mEvent = event;
+            mRawEvent = rawEvent;
+            mPolicyFlags = policyFlags;
+            mHandler.postDelayed(this, delay);
+            if (DEBUG) {
+                Slog.d(
+                        LOG_TAG,
+                        getGestureName()
+                                + ": posting delayed transition to "
+                                + getStateSymbolicName(mTargetState));
+            }
+        }
+
+        public boolean isPending() {
+            return mHandler.hasCallbacks(this);
+        }
+
+        public void forceSendAndRemove() {
+            if (isPending()) {
+                run();
+                cancel();
+            }
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG) {
+                Slog.d(
+                        LOG_TAG,
+                        getGestureName()
+                                + ": executing delayed transition to "
+                                + getStateSymbolicName(mTargetState));
+            }
+            setState(mTargetState, mEvent, mRawEvent, mPolicyFlags);
+        }
+    }
+
+    /** Interface to allow a class to listen for state changes in a specific gesture matcher */
+    interface StateChangeListener {
+
+        void onStateChanged(
+                int gestureId, int state, MotionEvent event, MotionEvent rawEvent, int policyFlags);
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java
new file mode 100644
index 0000000..2891c6c
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java
@@ -0,0 +1,145 @@
+/*
+ * 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.accessibility.gestures;
+
+import android.content.Context;
+import android.os.Handler;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+/**
+ * This class matches multi-tap gestures. The number of taps for each instance is specified in the
+ * constructor.
+ */
+class MultiTap extends GestureMatcher {
+
+    // Maximum reasonable number of taps.
+    public static final int MAX_TAPS = 10;
+    final int mTargetTaps;
+    // The acceptable distance between two taps
+    int mDoubleTapSlop;
+    // The acceptable distance the pointer can move and still count as a tap.
+    int mTouchSlop;
+    int mTapTimeout;
+    int mDoubleTapTimeout;
+    int mCurrentTaps;
+    float mBaseX;
+    float mBaseY;
+
+    MultiTap(Context context, int taps, int gesture, GestureMatcher.StateChangeListener listener) {
+        super(gesture, new Handler(context.getMainLooper()), listener);
+        mTargetTaps = taps;
+        mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
+        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        mTapTimeout = ViewConfiguration.getTapTimeout();
+        mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
+        clear();
+    }
+
+    @Override
+    protected void clear() {
+        mCurrentTaps = 0;
+        mBaseX = Float.NaN;
+        mBaseY = Float.NaN;
+        super.clear();
+    }
+
+    @Override
+    protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        cancelAfterTapTimeout(event, rawEvent, policyFlags);
+        if (Float.isNaN(mBaseX) && Float.isNaN(mBaseY)) {
+            mBaseX = event.getX();
+            mBaseY = event.getY();
+        }
+        if (!isInsideSlop(rawEvent, mDoubleTapSlop)) {
+            cancelGesture(event, rawEvent, policyFlags);
+        }
+        mBaseX = event.getX();
+        mBaseY = event.getY();
+    }
+
+    @Override
+    protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags);
+        if (!isInsideSlop(rawEvent, mTouchSlop)) {
+            cancelGesture(event, rawEvent, policyFlags);
+        }
+        if (getState() == STATE_GESTURE_STARTED || getState() == STATE_CLEAR) {
+            mCurrentTaps++;
+            if (mCurrentTaps == mTargetTaps) {
+                // Done.
+                completeAfterTapTimeout(event, rawEvent, policyFlags);
+                return;
+            }
+            // Needs more taps.
+            cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags);
+        } else {
+            // Either too many taps or nonsensical event stream.
+            cancelGesture(event, rawEvent, policyFlags);
+        }
+    }
+
+    @Override
+    protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (!isInsideSlop(rawEvent, mTouchSlop)) {
+            cancelGesture(event, rawEvent, policyFlags);
+        }
+    }
+
+    @Override
+    protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        cancelGesture(event, rawEvent, policyFlags);
+    }
+
+    @Override
+    protected void onPointerUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        cancelGesture(event, rawEvent, policyFlags);
+    }
+
+    @Override
+    public String getGestureName() {
+        switch (mTargetTaps) {
+            case 2:
+                return "Double Tap";
+            case 3:
+                return "Triple Tap";
+            default:
+                return Integer.toString(mTargetTaps) + " Taps";
+        }
+    }
+
+    private boolean isInsideSlop(MotionEvent rawEvent, int slop) {
+        final float deltaX = mBaseX - rawEvent.getX();
+        final float deltaY = mBaseY - rawEvent.getY();
+        if (deltaX == 0 && deltaY == 0) {
+            return true;
+        }
+        final double moveDelta = Math.hypot(deltaX, deltaY);
+        return moveDelta <= slop;
+    }
+
+    @Override
+    public String toString() {
+        return super.toString()
+                + ", Taps:"
+                + mCurrentTaps
+                + ", mBaseX: "
+                + Float.toString(mBaseX)
+                + ", mBaseY: "
+                + Float.toString(mBaseY);
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java
new file mode 100644
index 0000000..6a1f1a5
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.gestures;
+
+import android.content.Context;
+import android.view.MotionEvent;
+
+/**
+ * This class matches gestures of the form multi-tap and hold. The number of taps for each instance
+ * is specified in the constructor.
+ */
+class MultiTapAndHold extends MultiTap {
+    MultiTapAndHold(
+            Context context, int taps, int gesture, GestureMatcher.StateChangeListener listener) {
+        super(context, taps, gesture, listener);
+    }
+
+    @Override
+    protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        super.onDown(event, rawEvent, policyFlags);
+        if (mCurrentTaps + 1 == mTargetTaps) {
+            completeAfterLongPressTimeout(event, rawEvent, policyFlags);
+        }
+    }
+
+    @Override
+    public String getGestureName() {
+        switch (mTargetTaps) {
+            case 2:
+                return "Double Tap and Hold";
+            case 3:
+                return "Triple Tap and Hold";
+            default:
+                return Integer.toString(mTargetTaps) + " Taps and Hold";
+        }
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
new file mode 100644
index 0000000..b246c67
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
@@ -0,0 +1,430 @@
+/*
+ * 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.accessibility.gestures;
+
+import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
+
+import android.content.Context;
+import android.gesture.GesturePoint;
+import android.graphics.PointF;
+import android.os.Handler;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.MotionEvent;
+
+import java.util.ArrayList;
+
+/**
+ * This class is responsible for matching one-finger swipe gestures. Each instance matches one swipe
+ * gesture. A swipe is specified as a series of one or more directions e.g. left, left and up, etc.
+ * At this time swipes with more than two directions are not supported.
+ */
+class Swipe extends GestureMatcher {
+
+    // Direction constants.
+    public static final int LEFT = 0;
+    public static final int RIGHT = 1;
+    public static final int UP = 2;
+    public static final int DOWN = 3;
+    // This is the calculated movement threshold used track if the user is still
+    // moving their finger.
+    private final float mGestureDetectionThreshold;
+
+    // Buffer for storing points for gesture detection.
+    private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
+
+    // The minimal delta between moves to add a gesture point.
+    private static final int TOUCH_TOLERANCE_PIX = 3;
+
+    // The minimal score for accepting a predicted gesture.
+    private static final float MIN_PREDICTION_SCORE = 2.0f;
+
+    // Distance a finger must travel before we decide if it is a gesture or not.
+    private static final int GESTURE_CONFIRM_CM = 1;
+
+    // Time threshold used to determine if an interaction is a gesture or not.
+    // If the first movement of 1cm takes longer than this value, we assume it's
+    // a slow movement, and therefore not a gesture.
+    //
+    // This value was determined by measuring the time for the first 1cm
+    // movement when gesturing, and touch exploring.  Based on user testing,
+    // all gestures started with the initial movement taking less than 100ms.
+    // When touch exploring, the first movement almost always takes longer than
+    // 200ms.
+    private static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 150;
+
+    // Time threshold used to determine if a gesture should be cancelled.  If
+    // the finger takes more than this time to move 1cm, the ongoing gesture is
+    // cancelled.
+    private static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 300;
+
+    private int[] mDirections;
+    private float mBaseX;
+    private float mBaseY;
+    private long mBaseTime;
+    private float mPreviousGestureX;
+    private float mPreviousGestureY;
+    // Constants for sampling motion event points.
+    // We sample based on a minimum distance between points, primarily to improve accuracy by
+    // reducing noisy minor changes in direction.
+    private static final float MIN_CM_BETWEEN_SAMPLES = 0.25f;
+    private final float mMinPixelsBetweenSamplesX;
+    private final float mMinPixelsBetweenSamplesY;
+
+    // Constants for separating gesture segments
+    private static final float ANGLE_THRESHOLD = 0.0f;
+
+    Swipe(
+            Context context,
+            int direction,
+            int gesture,
+            GestureMatcher.StateChangeListener listener) {
+        this(context, new int[] {direction}, gesture, listener);
+    }
+
+    Swipe(
+            Context context,
+            int direction1,
+            int direction2,
+            int gesture,
+            GestureMatcher.StateChangeListener listener) {
+        this(context, new int[] {direction1, direction2}, gesture, listener);
+    }
+
+    private Swipe(
+            Context context,
+            int[] directions,
+            int gesture,
+            GestureMatcher.StateChangeListener listener) {
+        super(gesture, new Handler(context.getMainLooper()), listener);
+        mDirections = directions;
+        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+        mGestureDetectionThreshold =
+                TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 10, displayMetrics)
+                        * GESTURE_CONFIRM_CM;
+        // Calculate minimum gesture velocity
+        final float pixelsPerCmX = displayMetrics.xdpi / 2.54f;
+        final float pixelsPerCmY = displayMetrics.ydpi / 2.54f;
+        mMinPixelsBetweenSamplesX = MIN_CM_BETWEEN_SAMPLES * pixelsPerCmX;
+        mMinPixelsBetweenSamplesY = MIN_CM_BETWEEN_SAMPLES * pixelsPerCmY;
+        clear();
+    }
+
+    @Override
+    protected void clear() {
+        mBaseX = Float.NaN;
+        mBaseY = Float.NaN;
+        mBaseTime = 0;
+        mStrokeBuffer.clear();
+        super.clear();
+    }
+
+    @Override
+    protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        cancelAfterDelay(event, rawEvent, policyFlags);
+        if (Float.isNaN(mBaseX) && Float.isNaN(mBaseY)) {
+            mBaseX = rawEvent.getX();
+            mBaseY = rawEvent.getY();
+            mBaseTime = event.getEventTime();
+            mPreviousGestureX = mBaseX;
+            mPreviousGestureY = mBaseY;
+        }
+        // Otherwise do nothing because this event doesn't make sense in the middle of a gesture.
+    }
+
+    @Override
+    protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        final float x = rawEvent.getX();
+        final float y = rawEvent.getY();
+        final long time = event.getEventTime();
+        final float dX = Math.abs(x - mPreviousGestureX);
+        final float dY = Math.abs(y - mPreviousGestureY);
+        final long timeDelta = time - mBaseTime;
+        final double moveDelta = Math.hypot(Math.abs(x - mBaseX), Math.abs(y - mBaseY));
+        if (DEBUG) {
+            Slog.d(
+                    getGestureName(),
+                    "moveDelta:"
+                            + Double.toString(moveDelta)
+                            + " mGestureDetectionThreshold: "
+                            + Float.toString(mGestureDetectionThreshold));
+        }
+        if (getState() == STATE_CLEAR) {
+            if (mStrokeBuffer.size() == 0) {
+                // First, make sure the pointer is going in the right direction.
+                cancelAfterDelay(event, rawEvent, policyFlags);
+                int direction = toDirection(x - mBaseX, y - mBaseY);
+                if (direction != mDirections[0]) {
+                    cancelGesture(event, rawEvent, policyFlags);
+                    return;
+                } else {
+                    // This is confirmed to be some kind of swipe so start tracking points.
+                    mStrokeBuffer.add(new GesturePoint(mBaseX, mBaseY, mBaseTime));
+                }
+            }
+            if (moveDelta > mGestureDetectionThreshold) {
+                // If the pointer has moved more than the threshold,
+                // update the stored values.
+                mBaseX = x;
+                mBaseY = y;
+                mBaseTime = time;
+                if (getState() == STATE_CLEAR) {
+                    startGesture(event, rawEvent, policyFlags);
+                    cancelAfterDelay(event, rawEvent, policyFlags);
+                }
+            }
+        }
+        if (getState() == STATE_GESTURE_STARTED) {
+            if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
+                mPreviousGestureX = x;
+                mPreviousGestureY = y;
+                mStrokeBuffer.add(new GesturePoint(x, y, time));
+                cancelAfterDelay(event, rawEvent, policyFlags);
+            }
+        }
+    }
+
+    @Override
+    protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (getState() != STATE_GESTURE_STARTED) {
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+
+        final float x = rawEvent.getX();
+        final float y = rawEvent.getY();
+        final long time = event.getEventTime();
+        final float dX = Math.abs(x - mPreviousGestureX);
+        final float dY = Math.abs(y - mPreviousGestureY);
+        if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
+            mStrokeBuffer.add(new GesturePoint(x, y, time));
+        }
+        recognizeGesture(event, rawEvent, policyFlags);
+    }
+
+    @Override
+    protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        cancelGesture(event, rawEvent, policyFlags);
+    }
+
+    @Override
+    protected void onPointerUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        cancelGesture(event, rawEvent, policyFlags);
+    }
+
+    /**
+     * queues a transition to STATE_GESTURE_CANCEL based on the current state. If we have
+     * transitioned to STATE_GESTURE_STARTED the delay is longer.
+     */
+    private void cancelAfterDelay(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        cancelPendingTransitions();
+        switch (getState()) {
+            case STATE_CLEAR:
+                cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS, event, rawEvent, policyFlags);
+                break;
+            case STATE_GESTURE_STARTED:
+                cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS, event, rawEvent, policyFlags);
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Looks at the sequence of motions in mStrokeBuffer, classifies the gesture, then calls
+     * Listener callbacks for success or failure.
+     *
+     * @param event The raw motion event to pass to the listener callbacks.
+     * @param policyFlags Policy flags for the event.
+     * @return true if the event is consumed, else false
+     */
+    private void recognizeGesture(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (mStrokeBuffer.size() < 2) {
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+
+        // Look at mStrokeBuffer and extract 2 line segments, delimited by near-perpendicular
+        // direction change.
+        // Method: for each sampled motion event, check the angle of the most recent motion vector
+        // versus the preceding motion vector, and segment the line if the angle is about
+        // 90 degrees.
+
+        ArrayList<PointF> path = new ArrayList<>();
+        PointF lastDelimiter = new PointF(mStrokeBuffer.get(0).x, mStrokeBuffer.get(0).y);
+        path.add(lastDelimiter);
+
+        float dX = 0; // Sum of unit vectors from last delimiter to each following point
+        float dY = 0;
+        int count = 0; // Number of points since last delimiter
+        float length = 0; // Vector length from delimiter to most recent point
+
+        PointF next = new PointF();
+        for (int i = 1; i < mStrokeBuffer.size(); ++i) {
+            next = new PointF(mStrokeBuffer.get(i).x, mStrokeBuffer.get(i).y);
+            if (count > 0) {
+                // Average of unit vectors from delimiter to following points
+                float currentDX = dX / count;
+                float currentDY = dY / count;
+
+                // newDelimiter is a possible new delimiter, based on a vector with length from
+                // the last delimiter to the previous point, but in the direction of the average
+                // unit vector from delimiter to previous points.
+                // Using the averaged vector has the effect of "squaring off the curve",
+                // creating a sharper angle between the last motion and the preceding motion from
+                // the delimiter. In turn, this sharper angle achieves the splitting threshold
+                // even in a gentle curve.
+                PointF newDelimiter =
+                        new PointF(
+                                length * currentDX + lastDelimiter.x,
+                                length * currentDY + lastDelimiter.y);
+
+                // Unit vector from newDelimiter to the most recent point
+                float nextDX = next.x - newDelimiter.x;
+                float nextDY = next.y - newDelimiter.y;
+                float nextLength = (float) Math.sqrt(nextDX * nextDX + nextDY * nextDY);
+                nextDX = nextDX / nextLength;
+                nextDY = nextDY / nextLength;
+
+                // Compare the initial motion direction to the most recent motion direction,
+                // and segment the line if direction has changed by about 90 degrees.
+                float dot = currentDX * nextDX + currentDY * nextDY;
+                if (dot < ANGLE_THRESHOLD) {
+                    path.add(newDelimiter);
+                    lastDelimiter = newDelimiter;
+                    dX = 0;
+                    dY = 0;
+                    count = 0;
+                }
+            }
+
+            // Vector from last delimiter to most recent point
+            float currentDX = next.x - lastDelimiter.x;
+            float currentDY = next.y - lastDelimiter.y;
+            length = (float) Math.sqrt(currentDX * currentDX + currentDY * currentDY);
+
+            // Increment sum of unit vectors from delimiter to each following point
+            count = count + 1;
+            dX = dX + currentDX / length;
+            dY = dY + currentDY / length;
+        }
+
+        path.add(next);
+        if (DEBUG) {
+            Slog.d(getGestureName(), "path=" + path.toString());
+        }
+        // Classify line segments, and call Listener callbacks.
+        recognizeGesturePath(event, rawEvent, policyFlags, path);
+    }
+
+    /**
+     * Classifies a pair of line segments, by direction. Calls Listener callbacks for success or
+     * failure.
+     *
+     * @param event The raw motion event to pass to the listener's onGestureCanceled method.
+     * @param policyFlags Policy flags for the event.
+     * @param path A sequence of motion line segments derived from motion points in mStrokeBuffer.
+     * @return true if the event is consumed, else false
+     */
+    private void recognizeGesturePath(
+            MotionEvent event, MotionEvent rawEvent, int policyFlags, ArrayList<PointF> path) {
+
+        final int displayId = event.getDisplayId();
+        if (path.size() != mDirections.length + 1) {
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        for (int i = 0; i < path.size() - 1; ++i) {
+            PointF start = path.get(i);
+            PointF end = path.get(i + 1);
+
+            float dX = end.x - start.x;
+            float dY = end.y - start.y;
+            int direction = toDirection(dX, dY);
+            if (direction != mDirections[i]) {
+                if (DEBUG) {
+                    Slog.d(
+                            getGestureName(),
+                            "Found direction "
+                                    + directionToString(direction)
+                                    + " when expecting "
+                                    + directionToString(mDirections[i]));
+                }
+                cancelGesture(event, rawEvent, policyFlags);
+                return;
+            }
+        }
+        if (DEBUG) {
+            Slog.d(getGestureName(), "Completed.");
+        }
+        completeGesture(event, rawEvent, policyFlags);
+    }
+
+    private static int toDirection(float dX, float dY) {
+        if (Math.abs(dX) > Math.abs(dY)) {
+            // Horizontal
+            return (dX < 0) ? LEFT : RIGHT;
+        } else {
+            // Vertical
+            return (dY < 0) ? UP : DOWN;
+        }
+    }
+
+    public static String directionToString(int direction) {
+        switch (direction) {
+            case LEFT:
+                return "left";
+            case RIGHT:
+                return "right";
+            case UP:
+                return "up";
+            case DOWN:
+                return "down";
+            default:
+                return "Unknown Direction";
+        }
+    }
+
+    @Override
+    String getGestureName() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("Swipe ").append(directionToString(mDirections[0]));
+        for (int i = 1; i < mDirections.length; ++i) {
+            builder.append(" and ").append(directionToString(mDirections[i]));
+        }
+        return builder.toString();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder(super.toString());
+        if (getState() != STATE_GESTURE_CANCELED) {
+            builder.append(", mBaseX: ")
+                    .append(mBaseX)
+                    .append(", mBaseY: ")
+                    .append(mBaseY)
+                    .append(", mGestureDetectionThreshold:")
+                    .append(mGestureDetectionThreshold)
+                    .append(", mMinPixelsBetweenSamplesX:")
+                    .append(mMinPixelsBetweenSamplesX)
+                    .append(", mMinPixelsBetweenSamplesY:")
+                    .append(mMinPixelsBetweenSamplesY);
+        }
+        return builder.toString();
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index b62e260..5f41638 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -59,7 +59,7 @@
  * @hide
  */
 public class TouchExplorer extends BaseEventStreamTransformation
-        implements AccessibilityGestureDetector.Listener {
+        implements GestureManifold.Listener {
 
     static final boolean DEBUG = false;
 
@@ -104,7 +104,7 @@
     private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed;
 
     // Helper to detect gestures.
-    private final AccessibilityGestureDetector mGestureDetector;
+    private final GestureManifold  mGestureDetector;
 
     // Helper class to track received pointers.
     private final TouchState.ReceivedPointerTracker mReceivedPointerTracker;
@@ -142,7 +142,7 @@
      *                one created in place, or for testing purpose.
      */
     public TouchExplorer(Context context, AccessibilityManagerService service,
-            AccessibilityGestureDetector detector) {
+            GestureManifold detector) {
         mContext = context;
         mAms = service;
         mState = new TouchState();
@@ -161,7 +161,7 @@
                 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END,
                 mDetermineUserIntentTimeout);
         if (detector == null) {
-            mGestureDetector = new AccessibilityGestureDetector(context, this);
+            mGestureDetector = new GestureManifold(context, this, mState);
         } else {
             mGestureDetector = detector;
         }
@@ -285,7 +285,7 @@
     }
 
     @Override
-    public void onDoubleTapAndHold(MotionEvent event, int policyFlags) {
+    public void onDoubleTapAndHold() {
         // Ignore the event if we aren't touch interacting.
         if (!mState.isTouchInteracting()) {
             return;
@@ -303,7 +303,7 @@
     }
 
     @Override
-    public boolean onDoubleTap(MotionEvent event, int policyFlags) {
+    public boolean onDoubleTap() {
         if (!mState.isTouchInteracting()) {
             return false;
         }
@@ -319,7 +319,7 @@
 
         // Announce the end of a new touch interaction.
         mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
-
+        mSendTouchInteractionEndDelayed.cancel();
         // Try to use the standard accessibility API to click
         if (!mAms.performActionOnAccessibilityFocusedItem(
                 AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)) {
@@ -356,7 +356,7 @@
     }
 
     @Override
-    public boolean onGestureCancelled(MotionEvent event, int policyFlags) {
+    public boolean onGestureCancelled(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
         if (mState.isGestureDetecting()) {
             endGestureDetection(event.getActionMasked() == MotionEvent.ACTION_UP);
             return true;
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index f463260..d23dbbe 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -71,7 +71,10 @@
     // Helper class to track received pointers.
     // Todo: collapse or hide this class so multiple classes don't modify it.
     private final ReceivedPointerTracker mReceivedPointerTracker;
+    // The most recently received motion event.
     private MotionEvent mLastReceivedEvent;
+    // The accompanying raw event without any transformations.
+    private MotionEvent mLastReceivedRawEvent;
 
     public TouchState() {
         mReceivedPointerTracker = new ReceivedPointerTracker();
@@ -97,6 +100,9 @@
         if (mLastReceivedEvent != null) {
             mLastReceivedEvent.recycle();
         }
+        if (mLastReceivedRawEvent != null) {
+            mLastReceivedRawEvent.recycle();
+        }
         mLastReceivedEvent = MotionEvent.obtain(rawEvent);
         mReceivedPointerTracker.onMotionEvent(rawEvent);
     }
@@ -246,7 +252,6 @@
         // or if it goes up the next one that most recently went down.
         private int mPrimaryPointerId;
 
-
         ReceivedPointerTracker() {
             clear();
         }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 203bc61..b7adfa4 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -116,6 +116,7 @@
         "android.hardware.oemlock-V1.0-java",
         "android.hardware.configstore-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
+        "android.hardware.soundtrigger-V2.3-java",
         "android.hidl.manager-V1.2-java",
         "dnsresolver_aidl_interface-V2-java",
         "netd_event_listener_interface-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 53f306b..49046b2 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -275,6 +275,11 @@
     public abstract ComponentName getDefaultHomeActivity(int userId);
 
     /**
+     * @return The SystemUI service component name.
+     */
+    public abstract ComponentName getSystemUiServiceComponent();
+
+    /**
      * Called by DeviceOwnerManagerService to set the package names of device owner and profile
      * owners.
      */
@@ -336,14 +341,16 @@
      * @param responseObj The response of the first phase of ephemeral resolution
      * @param origIntent The original intent that triggered ephemeral resolution
      * @param resolvedType The resolved type of the intent
-     * @param callingPackage The name of the package requesting the ephemeral application
+     * @param callingPkg The app requesting the ephemeral application
+     * @param isRequesterInstantApp Whether or not the app requesting the ephemeral application
+     *                              is an instant app
      * @param verificationBundle Optional bundle to pass to the installer for additional
      * verification
      * @param userId The ID of the user that triggered ephemeral resolution
      */
     public abstract void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
-            Intent origIntent, String resolvedType, String callingPackage,
-            Bundle verificationBundle, int userId);
+            Intent origIntent, String resolvedType, String callingPkg,
+            boolean isRequesterInstantApp, Bundle verificationBundle, int userId);
 
     /**
      * Grants implicit access based on an interaction between two apps. This grants the target app
diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java
index a2e9341..d84197c 100644
--- a/services/core/java/android/os/UserManagerInternal.java
+++ b/services/core/java/android/os/UserManagerInternal.java
@@ -57,7 +57,7 @@
      * Called by {@link com.android.server.devicepolicy.DevicePolicyManagerService} to set
      * restrictions enforced by the user.
      *
-     * @param userId target user id for the local restrictions.
+     * @param originatingUserId user id of the user where the restriction originated.
      * @param restrictions a bundle of user restrictions.
      * @param restrictionOwnerType determines which admin {@code userId} corresponds to.
      *             The admin can be either
@@ -70,8 +70,8 @@
      *             otherwise it will be applied just on the current user.
      * @see OwnerType
      */
-    public abstract void setDevicePolicyUserRestrictions(int userId, @Nullable Bundle restrictions,
-            @OwnerType int restrictionOwnerType);
+    public abstract void setDevicePolicyUserRestrictions(int originatingUserId,
+            @Nullable Bundle restrictions, @OwnerType int restrictionOwnerType);
 
     /**
      * Returns the "base" user restrictions.
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 1e5b915..73b6c7a 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -22,6 +22,8 @@
 import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
 import static android.app.AlarmManager.RTC;
 import static android.app.AlarmManager.RTC_WAKEUP;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.os.UserHandle.USER_SYSTEM;
 
 import android.annotation.UserIdInt;
 import android.app.Activity;
@@ -41,10 +43,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PermissionInfo;
+import android.content.pm.PackageManagerInternal;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.BatteryManager;
@@ -223,15 +222,6 @@
     long mLastTimeChangeRealtime;
     int mNumTimeChanged;
 
-    // Bookkeeping about the identity of the "System UI" package, determined at runtime.
-
-    /**
-     * This permission must be defined by the canonical System UI package,
-     * with protection level "signature".
-     */
-    private static final String SYSTEM_UI_SELF_PERMISSION =
-            "android.permission.systemui.IDENTITY";
-
     /**
      * At boot we use SYSTEM_UI_SELF_PERMISSION to look up the definer's uid.
      */
@@ -3201,7 +3191,7 @@
     }
 
     void removeUserLocked(int userHandle) {
-        if (userHandle == UserHandle.USER_SYSTEM) {
+        if (userHandle == USER_SYSTEM) {
             // If we're told we're removing the system user, ignore it.
             return;
         }
@@ -3845,21 +3835,9 @@
         }
 
         int getSystemUiUid() {
-            int sysUiUid = -1;
-            final PackageManager pm = mContext.getPackageManager();
-            try {
-                PermissionInfo sysUiPerm = pm.getPermissionInfo(SYSTEM_UI_SELF_PERMISSION, 0);
-                ApplicationInfo sysUi = pm.getApplicationInfo(sysUiPerm.packageName, 0);
-                if ((sysUi.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
-                    sysUiUid = sysUi.uid;
-                } else {
-                    Slog.e(TAG, "SysUI permission " + SYSTEM_UI_SELF_PERMISSION
-                            + " defined by non-privileged app " + sysUi.packageName
-                            + " - ignoring");
-                }
-            } catch (NameNotFoundException e) {
-            }
-            return sysUiUid;
+            PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
+            return pm.getPackageUid(pm.getSystemUiServiceComponent().getPackageName(),
+                    MATCH_SYSTEM_ONLY, USER_SYSTEM);
         }
 
         ClockReceiver getClockReceiver(AlarmManagerService service) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 119b987..470300e 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -16,6 +16,9 @@
 
 package com.android.server;
 
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.os.UserHandle.USER_SYSTEM;
+
 import android.Manifest;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -42,6 +45,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.os.Binder;
@@ -242,10 +246,10 @@
                     }
 
                     // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user.
-                    if (userId == UserHandle.USER_SYSTEM
+                    if (userId == USER_SYSTEM
                             && UserRestrictionsUtils.restrictionsChanged(prevRestrictions,
                             newRestrictions, UserManager.DISALLOW_BLUETOOTH)) {
-                        if (userId == UserHandle.USER_SYSTEM && newRestrictions.getBoolean(
+                        if (userId == USER_SYSTEM && newRestrictions.getBoolean(
                                 UserManager.DISALLOW_BLUETOOTH)) {
                             updateOppLauncherComponentState(userId, true); // Sharing disallowed
                             sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_DISALLOWED,
@@ -437,18 +441,18 @@
         }
 
         int systemUiUid = -1;
-        try {
-            // Check if device is configured with no home screen, which implies no SystemUI.
-            boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);
-            if (!noHome) {
-                systemUiUid = mContext.getPackageManager()
-                        .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
-                                UserHandle.USER_SYSTEM);
-            }
+        // Check if device is configured with no home screen, which implies no SystemUI.
+        boolean noHome = mContext.getResources().getBoolean(R.bool.config_noHomeScreen);
+        if (!noHome) {
+            PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
+            systemUiUid = pm.getPackageUid(pm.getSystemUiServiceComponent().getPackageName(),
+                    MATCH_SYSTEM_ONLY, USER_SYSTEM);
+        }
+        if (systemUiUid >= 0) {
             Slog.d(TAG, "Detected SystemUiUid: " + Integer.toString(systemUiUid));
-        } catch (PackageManager.NameNotFoundException e) {
+        } else {
             // Some platforms, such as wearables do not have a system ui.
-            Slog.w(TAG, "Unable to resolve SystemUI's UID.", e);
+            Slog.w(TAG, "Unable to resolve SystemUI's UID.");
         }
         mSystemUiUid = systemUiUid;
     }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 4a0c7a3..0e144ed 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -31,7 +31,6 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.net.LinkProperties;
-import android.net.NetworkCapabilities;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -210,8 +209,6 @@
 
     private int[] mDataConnectionNetworkType;
 
-    private int[] mOtaspMode;
-
     private ArrayList<List<CellInfo>> mCellInfo = null;
 
     private Map<Integer, List<EmergencyNumber>> mEmergencyNumberList;
@@ -260,7 +257,9 @@
 
     private final LocalLog mListenLog = new LocalLog(100);
 
-    private PreciseDataConnectionState[] mPreciseDataConnectionState;
+    // Per-phoneMap of APN Type to DataConnectionState
+    private List<Map<String, PreciseDataConnectionState>> mPreciseDataConnectionStates =
+            new ArrayList<Map<String, PreciseDataConnectionState>>();
 
     // Nothing here yet, but putting it here in case we want to add more in the future.
     static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = 0;
@@ -405,7 +404,6 @@
         mCallForwarding = copyOf(mCallForwarding, mNumPhones);
         mCellLocation = copyOf(mCellLocation, mNumPhones);
         mSrvccState = copyOf(mSrvccState, mNumPhones);
-        mOtaspMode = copyOf(mOtaspMode, mNumPhones);
         mPreciseCallState = copyOf(mPreciseCallState, mNumPhones);
         mForegroundCallState = copyOf(mForegroundCallState, mNumPhones);
         mBackgroundCallState = copyOf(mBackgroundCallState, mNumPhones);
@@ -415,7 +413,6 @@
         mCallQuality = copyOf(mCallQuality, mNumPhones);
         mCallNetworkType = copyOf(mCallNetworkType, mNumPhones);
         mCallAttributes = copyOf(mCallAttributes, mNumPhones);
-        mPreciseDataConnectionState = copyOf(mPreciseDataConnectionState, mNumPhones);
         mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones);
         mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones);
 
@@ -423,6 +420,7 @@
         if (mNumPhones < oldNumPhones) {
             cutListToSize(mCellInfo, mNumPhones);
             cutListToSize(mImsReasonInfo, mNumPhones);
+            cutListToSize(mPreciseDataConnectionStates, mNumPhones);
             return;
         }
 
@@ -443,7 +441,6 @@
             mCellInfo.add(i, null);
             mImsReasonInfo.add(i, null);
             mSrvccState[i] = TelephonyManager.SRVCC_STATE_HANDOVER_NONE;
-            mOtaspMode[i] = TelephonyManager.OTASP_UNKNOWN;
             mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
             mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
             mCallQuality[i] = createCallQuality();
@@ -454,7 +451,7 @@
             mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
-            mPreciseDataConnectionState[i] = new PreciseDataConnectionState();
+            mPreciseDataConnectionStates.add(new HashMap<String, PreciseDataConnectionState>());
         }
 
         // Note that location can be null for non-phone builds like
@@ -506,7 +503,6 @@
         mCallForwarding = new boolean[numPhones];
         mCellLocation = new Bundle[numPhones];
         mSrvccState = new int[numPhones];
-        mOtaspMode = new int[numPhones];
         mPreciseCallState = new PreciseCallState[numPhones];
         mForegroundCallState = new int[numPhones];
         mBackgroundCallState = new int[numPhones];
@@ -516,7 +512,7 @@
         mCallQuality = new CallQuality[numPhones];
         mCallNetworkType = new int[numPhones];
         mCallAttributes = new CallAttributes[numPhones];
-        mPreciseDataConnectionState = new PreciseDataConnectionState[numPhones];
+        mPreciseDataConnectionStates = new ArrayList<>();
         mCellInfo = new ArrayList<>();
         mImsReasonInfo = new ArrayList<>();
         mEmergencyNumberList = new HashMap<>();
@@ -538,7 +534,6 @@
             mCellInfo.add(i, null);
             mImsReasonInfo.add(i, null);
             mSrvccState[i] = TelephonyManager.SRVCC_STATE_HANDOVER_NONE;
-            mOtaspMode[i] = TelephonyManager.OTASP_UNKNOWN;
             mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
             mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
             mCallQuality[i] = createCallQuality();
@@ -549,7 +544,7 @@
             mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
-            mPreciseDataConnectionState[i] = new PreciseDataConnectionState();
+            mPreciseDataConnectionStates.add(new HashMap<String, PreciseDataConnectionState>());
         }
 
         // Note that location can be null for non-phone builds like
@@ -880,13 +875,6 @@
                             remove(r.binder);
                         }
                     }
-                    if ((events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) {
-                        try {
-                            r.callback.onOtaspChanged(mOtaspMode[phoneId]);
-                        } catch (RemoteException ex) {
-                            remove(r.binder);
-                        }
-                    }
                     if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) {
                         try {
                             if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
@@ -922,8 +910,10 @@
                     }
                     if ((events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
                         try {
-                            r.callback.onPreciseDataConnectionStateChanged(
-                                    mPreciseDataConnectionState[phoneId]);
+                            for (PreciseDataConnectionState pdcs
+                                    : mPreciseDataConnectionStates.get(phoneId).values()) {
+                                r.callback.onPreciseDataConnectionStateChanged(pdcs);
+                            }
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -937,7 +927,8 @@
                     }
                     if ((events & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) !=0) {
                         try {
-                            r.callback.onVoiceActivationStateChanged(mVoiceActivationState[phoneId]);
+                            r.callback.onVoiceActivationStateChanged(
+                                    mVoiceActivationState[phoneId]);
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -1496,20 +1487,38 @@
         }
     }
 
-    public void notifyDataConnectionForSubscriber(int phoneId, int subId, int state,
-                                                  boolean isDataAllowed,
-                                                  String apn, String apnType,
-            LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int networkType, boolean roaming) {
+    /**
+     * Send a notification to registrants that the data connection state has changed.
+     *
+     * @param phoneId the phoneId carrying the data connection
+     * @param subId the subscriptionId for the data connection
+     * @param apnType the APN type that triggered a change in the data connection
+     * @param preciseState a PreciseDataConnectionState that has info about the data connection
+     */
+    public void notifyDataConnectionForSubscriber(
+            int phoneId, int subId, String apnType, PreciseDataConnectionState preciseState) {
         if (!checkNotifyPermission("notifyDataConnection()" )) {
             return;
         }
+
+        String apn = "";
+        int state = TelephonyManager.DATA_UNKNOWN;
+        int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        LinkProperties linkProps = null;
+
+        if (preciseState != null) {
+            apn = preciseState.getDataConnectionApn();
+            state = preciseState.getState();
+            networkType = preciseState.getNetworkType();
+            linkProps = preciseState.getDataConnectionLinkProperties();
+        }
         if (VDBG) {
             log("notifyDataConnectionForSubscriber: subId=" + subId
-                + " state=" + state + " isDataAllowed=" + isDataAllowed
-                + "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType
-                + " mRecords.size()=" + mRecords.size());
+                    + " state=" + state + "' apn='" + apn
+                    + "' apnType=" + apnType + " networkType=" + networkType
+                    + "' preciseState=" + preciseState);
         }
+
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
                 // We only call the callback when the change is for default APN type.
@@ -1541,30 +1550,48 @@
                     mDataConnectionState[phoneId] = state;
                     mDataConnectionNetworkType[phoneId] = networkType;
                 }
-                mPreciseDataConnectionState[phoneId] = new PreciseDataConnectionState(
-                        state, networkType,
-                        ApnSetting.getApnTypesBitmaskFromString(apnType), apn,
-                        linkProperties, DataFailCause.NONE);
-                for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
-                            && idMatch(r.subId, subId, phoneId)) {
-                        try {
-                            r.callback.onPreciseDataConnectionStateChanged(
-                                    mPreciseDataConnectionState[phoneId]);
-                        } catch (RemoteException ex) {
-                            mRemoveList.add(r.binder);
+
+                boolean needsNotify = false;
+                // State has been cleared for this APN Type
+                if (preciseState == null) {
+                    // We try clear the state and check if the state was previously not cleared
+                    needsNotify = mPreciseDataConnectionStates.get(phoneId).remove(apnType) != null;
+                } else {
+                    // We need to check to see if the state actually changed
+                    PreciseDataConnectionState oldPreciseState =
+                            mPreciseDataConnectionStates.get(phoneId).put(apnType, preciseState);
+                    needsNotify = !preciseState.equals(oldPreciseState);
+                }
+
+                if (needsNotify) {
+                    for (Record r : mRecords) {
+                        if (r.matchPhoneStateListenerEvent(
+                                PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
+                                && idMatch(r.subId, subId, phoneId)) {
+                            try {
+                                r.callback.onPreciseDataConnectionStateChanged(preciseState);
+                            } catch (RemoteException ex) {
+                                mRemoveList.add(r.binder);
+                            }
                         }
                     }
                 }
             }
             handleRemoveListLocked();
         }
-        broadcastDataConnectionStateChanged(state, isDataAllowed, apn, apnType, linkProperties,
-                networkCapabilities, roaming, subId);
+
+        broadcastDataConnectionStateChanged(state, apn, apnType, subId);
     }
 
-    public void notifyDataConnectionFailedForSubscriber(int phoneId, int subId, String apnType) {
+    /**
+     * Stub to satisfy the ITelephonyRegistry aidl interface; do not use this function.
+     * @see #notifyDataConnectionFailedForSubscriber
+     */
+    public void notifyDataConnectionFailed(String apnType) {
+        loge("This function should not be invoked");
+    }
+
+    private void notifyDataConnectionFailedForSubscriber(int phoneId, int subId, String apnType) {
         if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
             return;
         }
@@ -1574,17 +1601,20 @@
         }
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
-                mPreciseDataConnectionState[phoneId] = new PreciseDataConnectionState(
-                        TelephonyManager.DATA_UNKNOWN,TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                        ApnSetting.getApnTypesBitmaskFromString(apnType), null, null,
-                        DataFailCause.NONE);
+                mPreciseDataConnectionStates.get(phoneId).put(
+                        apnType,
+                        new PreciseDataConnectionState(
+                                TelephonyManager.DATA_UNKNOWN,
+                                TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                                ApnSetting.getApnTypesBitmaskFromString(apnType), null, null,
+                                DataFailCause.NONE));
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
                             PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onPreciseDataConnectionStateChanged(
-                                    mPreciseDataConnectionState[phoneId]);
+                                    mPreciseDataConnectionStates.get(phoneId).get(apnType));
                         } catch (RemoteException ex) {
                             mRemoveList.add(r.binder);
                         }
@@ -1635,29 +1665,6 @@
         }
     }
 
-    public void notifyOtaspChanged(int subId, int otaspMode) {
-        if (!checkNotifyPermission("notifyOtaspChanged()" )) {
-            return;
-        }
-        int phoneId = SubscriptionManager.getPhoneId(subId);
-        synchronized (mRecords) {
-            if (validatePhoneId(phoneId)) {
-                mOtaspMode[phoneId] = otaspMode;
-                for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_OTASP_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
-                        try {
-                            r.callback.onOtaspChanged(otaspMode);
-                        } catch (RemoteException ex) {
-                            mRemoveList.add(r.binder);
-                        }
-                    }
-                }
-            }
-            handleRemoveListLocked();
-        }
-    }
-
     public void notifyPreciseCallState(int phoneId, int subId, int ringingCallState,
                                        int foregroundCallState, int backgroundCallState) {
         if (!checkNotifyPermission("notifyPreciseCallState()")) {
@@ -1772,25 +1779,32 @@
         if (!checkNotifyPermission("notifyPreciseDataConnectionFailed()")) {
             return;
         }
+
+        // precise notify invokes imprecise notify
+        notifyDataConnectionFailedForSubscriber(phoneId, subId, apnType);
+
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
-                mPreciseDataConnectionState[phoneId] = new PreciseDataConnectionState(
-                        TelephonyManager.DATA_UNKNOWN, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                        ApnSetting.getApnTypesBitmaskFromString(apnType), apn, null, failCause);
+                mPreciseDataConnectionStates.get(phoneId).put(
+                        apnType,
+                        new PreciseDataConnectionState(
+                                TelephonyManager.DATA_UNKNOWN,
+                                TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                                ApnSetting.getApnTypesBitmaskFromString(apnType), null, null,
+                                failCause));
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
                             PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             r.callback.onPreciseDataConnectionStateChanged(
-                                    mPreciseDataConnectionState[phoneId]);
+                                    mPreciseDataConnectionStates.get(phoneId).get(apnType));
                         } catch (RemoteException ex) {
                             mRemoveList.add(r.binder);
                         }
                     }
                 }
             }
-
             handleRemoveListLocked();
         }
     }
@@ -2048,7 +2062,6 @@
         }
     }
 
-
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
@@ -2082,12 +2095,11 @@
                 pw.println("mCellInfo=" + mCellInfo.get(i));
                 pw.println("mImsCallDisconnectCause=" + mImsReasonInfo.get(i));
                 pw.println("mSrvccState=" + mSrvccState[i]);
-                pw.println("mOtaspMode=" + mOtaspMode[i]);
                 pw.println("mCallPreciseDisconnectCause=" + mCallPreciseDisconnectCause[i]);
                 pw.println("mCallQuality=" + mCallQuality[i]);
                 pw.println("mCallAttributes=" + mCallAttributes[i]);
                 pw.println("mCallNetworkType=" + mCallNetworkType[i]);
-                pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState[i]);
+                pw.println("mPreciseDataConnectionStates=" + mPreciseDataConnectionStates.get(i));
                 pw.println("mOutgoingCallEmergencyNumber=" + mOutgoingCallEmergencyNumber[i]);
                 pw.println("mOutgoingSmsEmergencyNumber=" + mOutgoingSmsEmergencyNumber[i]);
                 pw.decreaseIndent();
@@ -2247,29 +2259,13 @@
         }
     }
 
-    private void broadcastDataConnectionStateChanged(int state, boolean isDataAllowed, String apn,
-                                                     String apnType, LinkProperties linkProperties,
-                                                     NetworkCapabilities networkCapabilities,
-                                                     boolean roaming, int subId) {
+    private void broadcastDataConnectionStateChanged(int state, String apn,
+                                                     String apnType, int subId) {
         // Note: not reporting to the battery stats service here, because the
         // status bar takes care of that after taking into account all of the
         // required info.
         Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
         intent.putExtra(TelephonyManager.EXTRA_STATE, dataStateToString(state));
-        if (!isDataAllowed) {
-            intent.putExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY, true);
-        }
-        if (linkProperties != null) {
-            intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties);
-            String iface = linkProperties.getInterfaceName();
-            if (iface != null) {
-                intent.putExtra(PhoneConstants.DATA_IFACE_NAME_KEY, iface);
-            }
-        }
-        if (networkCapabilities != null) {
-            intent.putExtra(PhoneConstants.DATA_NETWORK_CAPABILITIES_KEY, networkCapabilities);
-        }
-        if (roaming) intent.putExtra(PhoneConstants.DATA_NETWORK_ROAMING_KEY, true);
 
         intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
         intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 3330882..f6c11cd 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -25,6 +25,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.hardware.input.InputManager;
@@ -79,7 +80,6 @@
         implements InputManager.InputDeviceListener {
     private static final String TAG = "VibratorService";
     private static final boolean DEBUG = false;
-    private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
     private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
     private static final String RAMPING_RINGER_ENABLED = "ramping_ringer_enabled";
 
@@ -139,6 +139,7 @@
     private final PowerManager.WakeLock mWakeLock;
     private final AppOpsManager mAppOps;
     private final IBatteryStats mBatteryStatsService;
+    private final String mSystemUiPackage;
     private PowerManagerInternal mPowerManagerInternal;
     private InputManager mIm;
     private Vibrator mVibrator;
@@ -284,7 +285,7 @@
         }
 
         public boolean isFromSystem() {
-            return uid == Process.SYSTEM_UID || uid == 0 || SYSTEM_UI_PACKAGE.equals(opPkg);
+            return uid == Process.SYSTEM_UID || uid == 0 || mSystemUiPackage.equals(opPkg);
         }
 
         public VibrationInfo toInfo() {
@@ -372,6 +373,8 @@
         mAppOps = mContext.getSystemService(AppOpsManager.class);
         mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
                 BatteryStats.SERVICE_NAME));
+        mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
+                .getSystemUiServiceComponent().getPackageName();
 
         mPreviousVibrationsLimit = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_previousVibrationsDumpLimit);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index e101fe0..5996b7d 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -4409,7 +4409,6 @@
         return true;
     }
 
-    @Override
     public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
         userId = handleIncomingUser(userId);
         UserAccounts accounts = getUserAccounts(userId);
@@ -4425,7 +4424,6 @@
         return r > 0;
     }
 
-    @Override
     public boolean removeSharedAccountAsUser(Account account, int userId) {
         return removeSharedAccountAsUser(account, userId, getCallingUid());
     }
@@ -4443,7 +4441,6 @@
         return deleted;
     }
 
-    @Override
     public Account[] getSharedAccountsAsUser(int userId) {
         userId = handleIncomingUser(userId);
         UserAccounts accounts = getUserAccounts(userId);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b55d6ad..f1cee034 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8389,6 +8389,18 @@
         return BugReportHandlerUtil.launchBugReportHandlerApp(mContext);
     }
 
+    /**
+     * Get packages of bugreport-whitelisted apps to handle a bug report.
+     *
+     * @return packages of bugreport-whitelisted apps to handle a bug report.
+     */
+    @Override
+    public List<String> getBugreportWhitelistedPackages() {
+        enforceCallingPermission(android.Manifest.permission.MANAGE_DEBUGGING,
+                "getBugreportWhitelistedPackages");
+        return new ArrayList<>(SystemConfig.getInstance().getBugreportWhitelistedPackages());
+    }
+
     public void registerProcessObserver(IProcessObserver observer) {
         enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
                 "registerProcessObserver()");
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 232bc08e..fc67e24 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -77,21 +77,28 @@
     private static final String LOG_TAG = "AttentionManagerService";
     private static final boolean DEBUG = false;
 
-    /** Default value in absence of {@link DeviceConfig} override. */
-    private static final boolean DEFAULT_SERVICE_ENABLED = true;
-
     /** Service will unbind if connection is not used for that amount of time. */
     private static final long CONNECTION_TTL_MILLIS = 60_000;
 
-    /** If the check attention called within that period - cached value will be returned. */
-    private static final long STALE_AFTER_MILLIS = 5_000;
+    /** DeviceConfig flag name, if {@code true}, enables AttentionManagerService features. */
+    private static final String KEY_SERVICE_ENABLED = "service_enabled";
+
+    /** Default value in absence of {@link DeviceConfig} override. */
+    private static final boolean DEFAULT_SERVICE_ENABLED = true;
+
+    /**
+     * DeviceConfig flag name, describes how much time we consider a result fresh; if the check
+     * attention called within that period - cached value will be returned.
+     */
+    @VisibleForTesting static final String KEY_STALE_AFTER_MILLIS = "stale_after_millis";
+
+    /** Default value in absence of {@link DeviceConfig} override. */
+    @VisibleForTesting static final long DEFAULT_STALE_AFTER_MILLIS = 1_000;
 
     /** The size of the buffer that stores recent attention check results. */
     @VisibleForTesting
     protected static final int ATTENTION_CACHE_BUFFER_SIZE = 5;
 
-    /** DeviceConfig flag name, if {@code true}, enables AttentionManagerService features. */
-    private static final String SERVICE_ENABLED = "service_enabled";
     private static String sTestAttentionServicePackage;
     private final Context mContext;
     private final PowerManager mPowerManager;
@@ -160,11 +167,29 @@
 
     @VisibleForTesting
     protected boolean isServiceEnabled() {
-        return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, SERVICE_ENABLED,
+        return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, KEY_SERVICE_ENABLED,
                 DEFAULT_SERVICE_ENABLED);
     }
 
     /**
+     * How much time we consider a result fresh; if the check attention called within that period -
+     * cached value will be returned.
+     */
+    @VisibleForTesting
+    protected long getStaleAfterMillis() {
+        final long millis = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_STALE_AFTER_MILLIS,
+                DEFAULT_STALE_AFTER_MILLIS);
+
+        if (millis < 0 || millis > 10_000) {
+            Slog.w(LOG_TAG, "Bad flag value supplied for: " + KEY_STALE_AFTER_MILLIS);
+            return DEFAULT_STALE_AFTER_MILLIS;
+        }
+
+        return millis;
+    }
+
+    /**
      * Checks whether user attention is at the screen and calls in the provided callback.
      *
      * Calling this multiple times quickly in a row will result in either a) returning a cached
@@ -199,7 +224,7 @@
             // throttle frequent requests
             final AttentionCheckCache cache = userState.mAttentionCheckCacheBuffer == null ? null
                     : userState.mAttentionCheckCacheBuffer.getLast();
-            if (cache != null && now < cache.mLastComputed + STALE_AFTER_MILLIS) {
+            if (cache != null && now < cache.mLastComputed + getStaleAfterMillis()) {
                 callbackInternal.onSuccess(cache.mResult, cache.mTimestamp);
                 return true;
             }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 8144a71..60f420e 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -600,19 +600,11 @@
         sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
     }
 
-    /*package*/ void cancelA2dpDockTimeout() {
-        mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
-    }
-
     /*package*/ void postA2dpActiveDeviceChange(
                     @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
         sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
     }
 
-    /*package*/ boolean hasScheduledA2dpDockTimeout() {
-        return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
-    }
-
     // must be called synchronized on mConnectedDevices
     /*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) {
         return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
@@ -621,8 +613,8 @@
                         new BtHelper.BluetoothA2dpDeviceInfo(btDevice)));
     }
 
-    /*package*/ void setA2dpDockTimeout(String address, int a2dpCodec, int delayMs) {
-        sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
+    /*package*/ void setA2dpTimeout(String address, int a2dpCodec, int delayMs) {
+        sendILMsg(MSG_IL_BTA2DP_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
     }
 
     /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
@@ -781,7 +773,7 @@
                         }
                     }
                     break;
-                case MSG_IL_BTA2DP_DOCK_TIMEOUT:
+                case MSG_IL_BTA2DP_TIMEOUT:
                     // msg.obj  == address of BTA2DP device
                     synchronized (mDeviceStateLock) {
                         mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
@@ -945,7 +937,7 @@
     private static final int MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE = 7;
     private static final int MSG_IL_SET_HEARING_AID_CONNECTION_STATE = 8;
     private static final int MSG_BT_HEADSET_CNCT_FAILED = 9;
-    private static final int MSG_IL_BTA2DP_DOCK_TIMEOUT = 10;
+    private static final int MSG_IL_BTA2DP_TIMEOUT = 10;
     private static final int MSG_L_A2DP_DEVICE_CONFIG_CHANGE = 11;
     private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 12;
     private static final int MSG_REPORT_NEW_ROUTES = 13;
@@ -981,7 +973,7 @@
             case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
             case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
             case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
-            case MSG_IL_BTA2DP_DOCK_TIMEOUT:
+            case MSG_IL_BTA2DP_TIMEOUT:
             case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
             case MSG_TOGGLE_HDMI:
             case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
@@ -1071,7 +1063,7 @@
                 case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
                 case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
                 case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
-                case MSG_IL_BTA2DP_DOCK_TIMEOUT:
+                case MSG_IL_BTA2DP_TIMEOUT:
                 case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
                 case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
                     if (sLastDeviceConnectMsgTime >= time) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 9061586..df56004 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -59,10 +59,21 @@
 
     private static final String TAG = "AS.AudioDeviceInventory";
 
-    // Actual list of connected devices
+    // lock to synchronize all access to mConnectedDevices and mApmConnectedDevices
+    private final Object mDevicesLock = new Object();
+
+    // List of connected devices
     // Key for map created from DeviceInfo.makeDeviceListKey()
+    @GuardedBy("mDevicesLock")
     private final LinkedHashMap<String, DeviceInfo> mConnectedDevices = new LinkedHashMap<>();
 
+    // List of devices actually connected to AudioPolicy (through AudioSystem), only one
+    // by device type, which is used as the key, value is the DeviceInfo generated key.
+    // For the moment only for A2DP sink devices.
+    // TODO: extend to all device types
+    @GuardedBy("mDevicesLock")
+    private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>();
+
     // List of preferred devices for strategies
     private final ArrayMap<Integer, AudioDeviceAddress> mPreferredDevices = new ArrayMap<>();
 
@@ -94,25 +105,30 @@
      */
     private static class DeviceInfo {
         final int mDeviceType;
-        final String mDeviceName;
-        final String mDeviceAddress;
+        final @NonNull String mDeviceName;
+        final @NonNull String mDeviceAddress;
         int mDeviceCodecFormat;
 
         DeviceInfo(int deviceType, String deviceName, String deviceAddress, int deviceCodecFormat) {
             mDeviceType = deviceType;
-            mDeviceName = deviceName;
-            mDeviceAddress = deviceAddress;
+            mDeviceName = deviceName == null ? "" : deviceName;
+            mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
             mDeviceCodecFormat = deviceCodecFormat;
         }
 
         @Override
         public String toString() {
             return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType)
-                    + " name:" + mDeviceName
+                    + " (" + AudioSystem.getDeviceName(mDeviceType)
+                    + ") name:" + mDeviceName
                     + " addr:" + mDeviceAddress
                     + " codec: " + Integer.toHexString(mDeviceCodecFormat) + "]";
         }
 
+        String getKey() {
+            return makeDeviceListKey(mDeviceType, mDeviceAddress);
+        }
+
         /**
          * Generate a unique key for the mConnectedDevices List by composing the device "type"
          * and the "address" associated with a specific instance of that device type
@@ -147,6 +163,14 @@
         pw.println("\n" + prefix + "Preferred devices for strategy:");
         mPreferredDevices.forEach((strategy, device) -> {
             pw.println("  " + prefix + "strategy:" + strategy + " device:" + device); });
+        pw.println("\n" + prefix + "Connected devices:");
+        mConnectedDevices.forEach((key, deviceInfo) -> {
+            pw.println("  " + prefix + deviceInfo.toString()); });
+        pw.println("\n" + prefix + "APM Connected device (A2DP sink only):");
+        mApmConnectedDevices.forEach((keyType, valueAddress) -> {
+            pw.println("  " + prefix + " type:0x" + Integer.toHexString(keyType)
+                    + " (" + AudioSystem.getDeviceName(keyType)
+                    + ") addr:" + valueAddress); });
     }
 
     //------------------------------------------------------------
@@ -158,7 +182,8 @@
      */
     // Always executed on AudioDeviceBroker message queue
     /*package*/ void onRestoreDevices() {
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
+            //TODO iterate on mApmConnectedDevices instead once it handles all device types
             for (DeviceInfo di : mConnectedDevices.values()) {
                 AudioSystem.setDeviceConnectionState(
                         di.mDeviceType,
@@ -168,7 +193,6 @@
                         di.mDeviceCodecFormat);
             }
         }
-
         synchronized (mPreferredDevices) {
             mPreferredDevices.forEach((strategy, device) -> {
                 AudioSystem.setPreferredDeviceForStrategy(strategy, device); });
@@ -187,6 +211,9 @@
                     + state + " vol=" + a2dpVolume);
         }
         String address = btDevice.getAddress();
+        if (address == null) {
+            address = "";
+        }
         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
             address = "";
         }
@@ -198,7 +225,7 @@
                         + " codec=" + a2dpCodec
                         + " vol=" + a2dpVolume));
 
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                     btDevice.getAddress());
             final DeviceInfo di = mConnectedDevices.get(key);
@@ -238,7 +265,7 @@
             address = "";
         }
 
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             final String key = DeviceInfo.makeDeviceListKey(
                     AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
             final DeviceInfo di = mConnectedDevices.get(key);
@@ -261,7 +288,7 @@
         AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
                 "onSetHearingAidConnectionState addr=" + address));
 
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID,
                     btDevice.getAddress());
             final DeviceInfo di = mConnectedDevices.get(key);
@@ -297,7 +324,7 @@
                 "onBluetoothA2dpActiveDeviceChange addr=" + address
                     + " event=" + BtHelper.a2dpDeviceEventToString(event)));
 
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             if (mDeviceBroker.hasScheduledA2dpSinkConnectionState(btDevice)) {
                 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
                         "A2dp config change ignored (scheduled connection change)"));
@@ -340,7 +367,7 @@
     }
 
     /*package*/ void onMakeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             makeA2dpDeviceUnavailableNow(address, a2dpCodec);
         }
     }
@@ -377,7 +404,7 @@
                             AudioDeviceInventory.WiredDeviceConnectionState wdcs) {
         AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs));
 
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED)
                     && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) {
                 mDeviceBroker.setBluetoothA2dpOnInt(true,
@@ -405,7 +432,7 @@
     }
 
     /*package*/ void onToggleHdmi() {
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             // Is HDMI connected?
             final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HDMI, "");
             final DeviceInfo di = mConnectedDevices.get(key);
@@ -472,7 +499,7 @@
                     + Integer.toHexString(device) + " address:" + address
                     + " name:" + deviceName + ")");
         }
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             final String deviceKey = DeviceInfo.makeDeviceListKey(device, address);
             if (AudioService.DEBUG_DEVICES) {
                 Slog.i(TAG, "deviceKey:" + deviceKey);
@@ -511,7 +538,7 @@
 
 
     /*package*/ void disconnectA2dp() {
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             final ArraySet<String> toRemove = new ArraySet<>();
             // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices
             mConnectedDevices.values().forEach(deviceInfo -> {
@@ -531,7 +558,7 @@
     }
 
     /*package*/ void disconnectA2dpSink() {
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             final ArraySet<String> toRemove = new ArraySet<>();
             // Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices
             mConnectedDevices.values().forEach(deviceInfo -> {
@@ -544,7 +571,7 @@
     }
 
     /*package*/ void disconnectHearingAid() {
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             final ArraySet<String> toRemove = new ArraySet<>();
             // Disconnect ALL DEVICE_OUT_HEARING_AID devices
             mConnectedDevices.values().forEach(deviceInfo -> {
@@ -568,7 +595,7 @@
     // from AudioSystem
     /*package*/ int checkSendBecomingNoisyIntent(int device,
             @AudioService.ConnectionState int state, int musicDevice) {
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             return checkSendBecomingNoisyIntentInt(device, state, musicDevice);
         }
     }
@@ -595,7 +622,7 @@
         if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
             throw new IllegalArgumentException("invalid profile " + profile);
         }
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) {
                 @AudioService.ConnectionState int asState =
                         (state == BluetoothA2dp.STATE_CONNECTED)
@@ -635,7 +662,7 @@
 
     /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state,
                                                   String address, String name, String caller) {
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             int delay = checkSendBecomingNoisyIntentInt(type, state, AudioSystem.DEVICE_NONE);
             mDeviceBroker.postSetWiredDeviceConnectionState(
                     new WiredDeviceConnectionState(type, state, address, name, caller),
@@ -648,7 +675,7 @@
             @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
             boolean suppressNoisyIntent, int musicDevice) {
         int delay;
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             if (!suppressNoisyIntent) {
                 int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0;
                 delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_HEARING_AID,
@@ -665,39 +692,58 @@
     //-------------------------------------------------------------------
     // Internal utilities
 
-    @GuardedBy("mConnectedDevices")
+    @GuardedBy("mDevicesLock")
     private void makeA2dpDeviceAvailable(String address, String name, String eventSource,
             int a2dpCodec) {
         // enable A2DP before notifying A2DP connection to avoid unnecessary processing in
         // audio policy manager
         mDeviceBroker.setBluetoothA2dpOnInt(true, eventSource);
+        // at this point there could be another A2DP device already connected in APM, but it
+        // doesn't matter as this new one will overwrite the previous one
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                 AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec);
         // Reset A2DP suspend state each time a new sink is connected
         AudioSystem.setParameters("A2dpSuspended=false");
-        mConnectedDevices.put(
-                DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address),
-                new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
-                        address, a2dpCodec));
+
+        final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
+                address, a2dpCodec);
+        final String diKey = di.getKey();
+        mConnectedDevices.put(diKey, di);
+        // on a connection always overwrite the device seen by AudioPolicy, see comment above when
+        // calling AudioSystem
+        mApmConnectedDevices.put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, diKey);
+
         mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
         setCurrentAudioRouteNameIfPossible(name);
     }
 
-    @GuardedBy("mConnectedDevices")
+    @GuardedBy("mDevicesLock")
     private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
         if (address == null) {
             return;
         }
+        final String deviceToRemoveKey =
+                DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
+
+        mConnectedDevices.remove(deviceToRemoveKey);
+        if (!mApmConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)
+                .equals(deviceToRemoveKey)) {
+            // removing A2DP device not currently used by AudioPolicy, log but don't act on it
+            AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
+                    "A2DP device " + address + " made unavailable, was not used")).printLog(TAG));
+            return;
+        }
+
+        // device to remove was visible by APM, update APM
         mDeviceBroker.setAvrcpAbsoluteVolumeSupported(false);
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                 AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec);
-        mConnectedDevices.remove(
-                DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
+        mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
         // Remove A2DP routes as well
         setCurrentAudioRouteNameIfPossible(null);
     }
 
-    @GuardedBy("mConnectedDevices")
+    @GuardedBy("mDevicesLock")
     private void makeA2dpDeviceUnavailableLater(String address, int delayMs) {
         // prevent any activity on the A2DP audio output to avoid unwanted
         // reconnection of the sink.
@@ -711,11 +757,11 @@
         // the device will be made unavailable later, so consider it disconnected right away
         mConnectedDevices.remove(deviceKey);
         // send the delayed message to make the device unavailable later
-        mDeviceBroker.setA2dpDockTimeout(address, a2dpCodec, delayMs);
+        mDeviceBroker.setA2dpTimeout(address, a2dpCodec, delayMs);
     }
 
 
-    @GuardedBy("mConnectedDevices")
+    @GuardedBy("mDevicesLock")
     private void makeA2dpSrcAvailable(String address) {
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
                 AudioSystem.DEVICE_STATE_AVAILABLE, address, "",
@@ -726,7 +772,7 @@
                         address, AudioSystem.AUDIO_FORMAT_DEFAULT));
     }
 
-    @GuardedBy("mConnectedDevices")
+    @GuardedBy("mDevicesLock")
     private void makeA2dpSrcUnavailable(String address) {
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
                 AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "",
@@ -735,7 +781,7 @@
                 DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
     }
 
-    @GuardedBy("mConnectedDevices")
+    @GuardedBy("mDevicesLock")
     private void makeHearingAidDeviceAvailable(
             String address, String name, int streamType, String eventSource) {
         final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
@@ -755,7 +801,7 @@
         setCurrentAudioRouteNameIfPossible(name);
     }
 
-    @GuardedBy("mConnectedDevices")
+    @GuardedBy("mDevicesLock")
     private void makeHearingAidDeviceUnavailable(String address) {
         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
                 AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "",
@@ -766,7 +812,7 @@
         setCurrentAudioRouteNameIfPossible(null);
     }
 
-    @GuardedBy("mConnectedDevices")
+    @GuardedBy("mDevicesLock")
     private void setCurrentAudioRouteNameIfPossible(String name) {
         synchronized (mCurAudioRoutes) {
             if (TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
@@ -779,7 +825,7 @@
         }
     }
 
-    @GuardedBy("mConnectedDevices")
+    @GuardedBy("mDevicesLock")
     private boolean isCurrentDeviceConnected() {
         return mConnectedDevices.values().stream().anyMatch(deviceInfo ->
             TextUtils.equals(deviceInfo.mDeviceName, mCurAudioRoutes.bluetoothName));
@@ -807,7 +853,7 @@
     // must be called before removing the device from mConnectedDevices
     // musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying
     // from AudioSystem
-    @GuardedBy("mConnectedDevices")
+    @GuardedBy("mDevicesLock")
     private int checkSendBecomingNoisyIntentInt(int device,
             @AudioService.ConnectionState int state, int musicDevice) {
         if (state != AudioService.CONNECTION_STATE_DISCONNECTED) {
@@ -1015,7 +1061,7 @@
     public boolean isA2dpDeviceConnected(@NonNull BluetoothDevice device) {
         final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                 device.getAddress());
-        synchronized (mConnectedDevices) {
+        synchronized (mDevicesLock) {
             return (mConnectedDevices.get(key) != null);
         }
     }
diff --git a/services/core/java/com/android/server/biometrics/OWNERS b/services/core/java/com/android/server/biometrics/OWNERS
new file mode 100644
index 0000000..8765c9a
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+kchyn@google.com
+jaggies@google.com
+curtislb@google.com
+ilyamaty@google.com
+joshmccloskey@google.com
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 9bae902..af8a366 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -39,11 +39,11 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.net.ISocketKeepaliveCallback;
+import android.net.InvalidPacketException;
 import android.net.KeepalivePacketData;
 import android.net.NattKeepalivePacketData;
 import android.net.NetworkAgent;
 import android.net.NetworkUtils;
-import android.net.SocketKeepalive.InvalidPacketException;
 import android.net.SocketKeepalive.InvalidSocketException;
 import android.net.TcpKeepalivePacketData;
 import android.net.util.IpUtils;
@@ -657,7 +657,10 @@
         final TcpKeepalivePacketData packet;
         try {
             packet = TcpKeepaliveController.getTcpKeepalivePacket(fd);
-        } catch (InvalidPacketException | InvalidSocketException e) {
+        } catch (InvalidSocketException e) {
+            notifyErrorCallback(cb, e.error);
+            return;
+        } catch (InvalidPacketException e) {
             notifyErrorCallback(cb, e.error);
             return;
         }
diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
index e570ef1e..1129899 100644
--- a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
+++ b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
@@ -30,8 +30,8 @@
 import static android.system.OsConstants.TIOCOUTQ;
 
 import android.annotation.NonNull;
+import android.net.InvalidPacketException;
 import android.net.NetworkUtils;
-import android.net.SocketKeepalive.InvalidPacketException;
 import android.net.SocketKeepalive.InvalidSocketException;
 import android.net.TcpKeepalivePacketData;
 import android.net.TcpKeepalivePacketDataParcelable;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 2cb7e3b..069aeef 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -47,7 +47,8 @@
 import android.media.Session2CommandGroup;
 import android.media.Session2Token;
 import android.media.session.IActiveSessionsListener;
-import android.media.session.ICallback;
+import android.media.session.IOnMediaKeyEventDispatchedListener;
+import android.media.session.IOnMediaKeyEventSessionChangedListener;
 import android.media.session.IOnMediaKeyListener;
 import android.media.session.IOnVolumeKeyLongPressListener;
 import android.media.session.ISession;
@@ -750,7 +751,10 @@
 
         private final int mFullUserId;
         private final MediaSessionStack mPriorityStack;
-        private final HashMap<IBinder, CallbackRecord> mCallbacks = new HashMap<>();
+        private final HashMap<IBinder, OnMediaKeyEventDispatchedListenerRecord>
+                mOnMediaKeyEventDispatchedListeners = new HashMap<>();
+        private final HashMap<IBinder, OnMediaKeyEventSessionChangedListenerRecord>
+                mOnMediaKeyEventSessionChangedListeners = new HashMap<>();
 
         private PendingIntent mLastMediaButtonReceiver;
         private ComponentName mRestoredMediaButtonReceiver;
@@ -796,21 +800,47 @@
             }
         }
 
-        public void registerCallbackLocked(ICallback callback, int uid) {
-            IBinder cbBinder = callback.asBinder();
-            CallbackRecord cr = new CallbackRecord(callback, uid);
-            mCallbacks.put(cbBinder, cr);
+        public void addOnMediaKeyEventDispatchedListenerLocked(
+                IOnMediaKeyEventDispatchedListener listener, int uid) {
+            IBinder cbBinder = listener.asBinder();
+            OnMediaKeyEventDispatchedListenerRecord cr =
+                    new OnMediaKeyEventDispatchedListenerRecord(listener, uid);
+            mOnMediaKeyEventDispatchedListeners.put(cbBinder, cr);
             try {
                 cbBinder.linkToDeath(cr, 0);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed to register callback", e);
-                mCallbacks.remove(cbBinder);
+                Log.w(TAG, "Failed to add listener", e);
+                mOnMediaKeyEventDispatchedListeners.remove(cbBinder);
             }
         }
 
-        public void unregisterCallbackLocked(ICallback callback) {
-            IBinder cbBinder = callback.asBinder();
-            CallbackRecord cr = mCallbacks.remove(cbBinder);
+        public void removeOnMediaKeyEventDispatchedListenerLocked(
+                IOnMediaKeyEventDispatchedListener listener) {
+            IBinder cbBinder = listener.asBinder();
+            OnMediaKeyEventDispatchedListenerRecord cr =
+                    mOnMediaKeyEventDispatchedListeners.remove(cbBinder);
+            cbBinder.unlinkToDeath(cr, 0);
+        }
+
+        public void addOnMediaKeyEventSessionChangedListenerLocked(
+                IOnMediaKeyEventSessionChangedListener listener, int uid) {
+            IBinder cbBinder = listener.asBinder();
+            OnMediaKeyEventSessionChangedListenerRecord cr =
+                    new OnMediaKeyEventSessionChangedListenerRecord(listener, uid);
+            mOnMediaKeyEventSessionChangedListeners.put(cbBinder, cr);
+            try {
+                cbBinder.linkToDeath(cr, 0);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to add listener", e);
+                mOnMediaKeyEventSessionChangedListeners.remove(cbBinder);
+            }
+        }
+
+        public void removeOnMediaKeyEventSessionChangedListener(
+                IOnMediaKeyEventSessionChangedListener listener) {
+            IBinder cbBinder = listener.asBinder();
+            OnMediaKeyEventSessionChangedListenerRecord cr =
+                    mOnMediaKeyEventSessionChangedListeners.remove(cbBinder);
             cbBinder.unlinkToDeath(cr, 0);
         }
 
@@ -832,8 +862,16 @@
             pw.println(indent + "Media key listener: " + mOnMediaKeyListener);
             pw.println(indent + "Media key listener package: "
                     + getCallingPackageName(mOnMediaKeyListenerUid));
-            pw.println(indent + "Callbacks: registered " + mCallbacks.size() + " callback(s)");
-            for (CallbackRecord cr : mCallbacks.values()) {
+            pw.println(indent + "OnMediaKeyEventDispatchedListener: added "
+                    + mOnMediaKeyEventDispatchedListeners.size() + " listener(s)");
+            for (OnMediaKeyEventDispatchedListenerRecord cr
+                    : mOnMediaKeyEventDispatchedListeners.values()) {
+                pw.println(indent + "  from " + getCallingPackageName(cr.uid));
+            }
+            pw.println(indent + "OnMediaKeyEventSessionChangedListener: added "
+                    + mOnMediaKeyEventSessionChangedListeners.size() + " listener(s)");
+            for (OnMediaKeyEventSessionChangedListenerRecord cr
+                    : mOnMediaKeyEventSessionChangedListeners.values()) {
                 pw.println(indent + "  from " + getCallingPackageName(cr.uid));
             }
             pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
@@ -895,19 +933,22 @@
                     mFullUserId);
         }
 
-        private void pushAddressedPlayerChangedLocked(ICallback callback) {
+        private void pushAddressedPlayerChangedLocked(
+                IOnMediaKeyEventSessionChangedListener callback) {
             try {
                 MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
                 if (mediaButtonSession != null) {
-                    callback.onAddressedPlayerChangedToMediaSession(
+                    callback.onMediaKeyEventSessionChanged(mediaButtonSession.getPackageName(),
                             mediaButtonSession.getSessionToken());
                 } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
-                    callback.onAddressedPlayerChangedToMediaButtonReceiver(
+                    callback.onMediaKeyEventSessionChanged(
                             mCurrentFullUserRecord.mLastMediaButtonReceiver
-                                    .getIntent().getComponent());
+                                    .getIntent().getComponent().getPackageName(),
+                            null);
                 } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
-                    callback.onAddressedPlayerChangedToMediaButtonReceiver(
-                            mCurrentFullUserRecord.mRestoredMediaButtonReceiver);
+                    callback.onMediaKeyEventSessionChanged(
+                            mCurrentFullUserRecord.mRestoredMediaButtonReceiver.getPackageName(),
+                            null);
                 }
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
@@ -915,7 +956,8 @@
         }
 
         private void pushAddressedPlayerChangedLocked() {
-            for (CallbackRecord cr : mCallbacks.values()) {
+            for (OnMediaKeyEventSessionChangedListenerRecord cr
+                    : mOnMediaKeyEventSessionChangedListeners.values()) {
                 pushAddressedPlayerChangedLocked(cr.callback);
             }
         }
@@ -954,11 +996,12 @@
             return COMPONENT_TYPE_BROADCAST;
         }
 
-        final class CallbackRecord implements IBinder.DeathRecipient {
-            public final ICallback callback;
+        final class OnMediaKeyEventDispatchedListenerRecord implements IBinder.DeathRecipient {
+            public final IOnMediaKeyEventDispatchedListener callback;
             public final int uid;
 
-            CallbackRecord(ICallback callback, int uid) {
+            OnMediaKeyEventDispatchedListenerRecord(IOnMediaKeyEventDispatchedListener callback,
+                    int uid) {
                 this.callback = callback;
                 this.uid = uid;
             }
@@ -966,7 +1009,25 @@
             @Override
             public void binderDied() {
                 synchronized (mLock) {
-                    mCallbacks.remove(callback.asBinder());
+                    mOnMediaKeyEventDispatchedListeners.remove(callback.asBinder());
+                }
+            }
+        }
+
+        final class OnMediaKeyEventSessionChangedListenerRecord implements IBinder.DeathRecipient {
+            public final IOnMediaKeyEventSessionChangedListener callback;
+            public final int uid;
+
+            OnMediaKeyEventSessionChangedListenerRecord(
+                    IOnMediaKeyEventSessionChangedListener callback, int uid) {
+                this.callback = callback;
+                this.uid = uid;
+            }
+
+            @Override
+            public void binderDied() {
+                synchronized (mLock) {
+                    mOnMediaKeyEventSessionChangedListeners.remove(callback.asBinder());
                 }
             }
         }
@@ -1355,7 +1416,8 @@
         }
 
         @Override
-        public void registerCallback(final ICallback callback) {
+        public void addOnMediaKeyEventDispatchedListener(
+                final IOnMediaKeyEventDispatchedListener listener) {
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             final int userId = UserHandle.getUserId(uid);
@@ -1363,18 +1425,18 @@
             try {
                 if (!hasMediaControlPermission(pid, uid)) {
                     throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to"
-                            + "  register Callback");
+                            + "  add MediaKeyEventDispatchedListener");
                 }
                 synchronized (mLock) {
                     FullUserRecord user = getFullUserRecordLocked(userId);
                     if (user == null || user.mFullUserId != userId) {
-                        Log.w(TAG, "Only the full user can register the callback"
+                        Log.w(TAG, "Only the full user can add the listener"
                                 + ", userId=" + userId);
                         return;
                     }
-                    user.registerCallbackLocked(callback, uid);
-                    Log.d(TAG, "The callback (" + callback.asBinder()
-                            + ") is registered by " + getCallingPackageName(uid));
+                    user.addOnMediaKeyEventDispatchedListenerLocked(listener, uid);
+                    Log.d(TAG, "The MediaKeyEventDispatchedListener (" + listener.asBinder()
+                            + ") is added by " + getCallingPackageName(uid));
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -1382,7 +1444,8 @@
         }
 
         @Override
-        public void unregisterCallback(final ICallback callback) {
+        public void removeOnMediaKeyEventDispatchedListener(
+                final IOnMediaKeyEventDispatchedListener listener) {
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             final int userId = UserHandle.getUserId(uid);
@@ -1390,18 +1453,74 @@
             try {
                 if (!hasMediaControlPermission(pid, uid)) {
                     throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to"
-                            + "  unregister Callback");
+                            + "  remove MediaKeyEventDispatchedListener");
                 }
                 synchronized (mLock) {
                     FullUserRecord user = getFullUserRecordLocked(userId);
                     if (user == null || user.mFullUserId != userId) {
-                        Log.w(TAG, "Only the full user can unregister the callback"
+                        Log.w(TAG, "Only the full user can remove the listener"
                                 + ", userId=" + userId);
                         return;
                     }
-                    user.unregisterCallbackLocked(callback);
-                    Log.d(TAG, "The callback (" + callback.asBinder()
-                            + ") is unregistered by " + getCallingPackageName(uid));
+                    user.removeOnMediaKeyEventDispatchedListenerLocked(listener);
+                    Log.d(TAG, "The MediaKeyEventDispatchedListener (" + listener.asBinder()
+                            + ") is removed by " + getCallingPackageName(uid));
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void addOnMediaKeyEventSessionChangedListener(
+                final IOnMediaKeyEventSessionChangedListener listener) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(uid);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (!hasMediaControlPermission(pid, uid)) {
+                    throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to"
+                            + "  add MediaKeyEventSessionChangedListener");
+                }
+                synchronized (mLock) {
+                    FullUserRecord user = getFullUserRecordLocked(userId);
+                    if (user == null || user.mFullUserId != userId) {
+                        Log.w(TAG, "Only the full user can add the listener"
+                                + ", userId=" + userId);
+                        return;
+                    }
+                    user.addOnMediaKeyEventSessionChangedListenerLocked(listener, uid);
+                    Log.d(TAG, "The MediaKeyEventSessionChangedListener (" + listener.asBinder()
+                            + ") is added by " + getCallingPackageName(uid));
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void removeOnMediaKeyEventSessionChangedListener(
+                final IOnMediaKeyEventSessionChangedListener listener) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(uid);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (!hasMediaControlPermission(pid, uid)) {
+                    throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to"
+                            + "  remove MediaKeyEventSessionChangedListener");
+                }
+                synchronized (mLock) {
+                    FullUserRecord user = getFullUserRecordLocked(userId);
+                    if (user == null || user.mFullUserId != userId) {
+                        Log.w(TAG, "Only the full user can remove the listener"
+                                + ", userId=" + userId);
+                        return;
+                    }
+                    user.removeOnMediaKeyEventSessionChangedListener(listener);
+                    Log.d(TAG, "The MediaKeyEventSessionChangedListener (" + listener.asBinder()
+                            + ") is removed by " + getCallingPackageName(uid));
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -2015,10 +2134,10 @@
                         needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
                         mKeyEventReceiver);
                 try {
-                    for (FullUserRecord.CallbackRecord cr
-                            : mCurrentFullUserRecord.mCallbacks.values()) {
-                        cr.callback.onMediaKeyEventDispatchedToMediaSession(
-                                keyEvent, session.getSessionToken());
+                    for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
+                            : mCurrentFullUserRecord.mOnMediaKeyEventDispatchedListeners.values()) {
+                        cr.callback.onMediaKeyEventDispatched(
+                                keyEvent, session.getPackageName(), session.getSessionToken());
                     }
                 } catch (RemoteException e) {
                     Log.w(TAG, "Failed to send callback", e);
@@ -2048,10 +2167,11 @@
                         ComponentName componentName = mCurrentFullUserRecord
                                 .mLastMediaButtonReceiver.getIntent().getComponent();
                         if (componentName != null) {
-                            for (FullUserRecord.CallbackRecord cr
-                                    : mCurrentFullUserRecord.mCallbacks.values()) {
-                                cr.callback.onMediaKeyEventDispatchedToMediaButtonReceiver(
-                                                keyEvent, componentName);
+                            for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
+                                    : mCurrentFullUserRecord
+                                    .mOnMediaKeyEventDispatchedListeners.values()) {
+                                cr.callback.onMediaKeyEventDispatched(keyEvent,
+                                        componentName.getPackageName(), null);
                             }
                         }
                     } else {
@@ -2083,10 +2203,11 @@
                             Log.w(TAG, "Error sending media button to the restored intent "
                                     + receiver + ", type=" + componentType, e);
                         }
-                        for (FullUserRecord.CallbackRecord cr
-                                : mCurrentFullUserRecord.mCallbacks.values()) {
-                            cr.callback.onMediaKeyEventDispatchedToMediaButtonReceiver(
-                                            keyEvent, receiver);
+                        for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
+                                : mCurrentFullUserRecord
+                                .mOnMediaKeyEventDispatchedListeners.values()) {
+                            cr.callback.onMediaKeyEventDispatched(keyEvent,
+                                    receiver.getPackageName(), null);
                         }
                     }
                 } catch (CanceledException e) {
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 7098435..a7e40cb 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -59,6 +59,12 @@
             return -1 * Boolean.compare(isLeftHighImportance, isRightHighImportance);
         }
 
+        // If a score has been assigned by notification assistant service, use this service
+        // rank results within each bucket instead of this comparator implementation.
+        if (left.getRankingScore() != right.getRankingScore()) {
+            return -1 * Float.compare(left.getRankingScore(), right.getRankingScore());
+        }
+
         // first all colorized notifications
         boolean leftImportantColorized = isImportantColorized(left);
         boolean rightImportantColorized = isImportantColorized(right);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index e968fb70..c8afcc9 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -144,6 +144,7 @@
     private int mSystemImportance = IMPORTANCE_UNSPECIFIED;
     private int mAssistantImportance = IMPORTANCE_UNSPECIFIED;
     private int mImportance = IMPORTANCE_UNSPECIFIED;
+    private float mRankingScore = 0f;
     // Field used in global sort key to bypass normal notifications
     private int mCriticality = CriticalNotificationExtractor.NORMAL;
     // A MetricsEvent.NotificationImportanceExplanation, tracking source of mImportance.
@@ -655,6 +656,9 @@
                     importance = Math.min(IMPORTANCE_HIGH, importance);
                     setAssistantImportance(importance);
                 }
+                if (signals.containsKey(Adjustment.KEY_RANKING_SCORE)) {
+                    mRankingScore = signals.getFloat(Adjustment.KEY_RANKING_SCORE);
+                }
                 if (!signals.isEmpty() && adjustment.getIssuer() != null) {
                     mAdjustmentIssuer = adjustment.getIssuer();
                 }
@@ -772,6 +776,10 @@
         return mImportance;
     }
 
+    public float getRankingScore() {
+        return mRankingScore;
+    }
+
     public CharSequence getImportanceExplanation() {
         switch (mImportanceExplanationCode) {
             case MetricsEvent.IMPORTANCE_EXPLANATION_UNKNOWN:
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index e055116..ac3bf9a 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -26,6 +26,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.text.TextUtils;
+import android.util.Pair;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
@@ -44,6 +45,38 @@
 
     private final VerifyCallback mVerifyCallback;
 
+    /**
+     * @return nullable actor result with {@link ActorState} failure status
+     */
+    static Pair<String, ActorState> getPackageNameForActor(String actorUriString,
+            Map<String, Map<String, String>> namedActors) {
+        if (namedActors.isEmpty()) {
+            return Pair.create(null, ActorState.NO_NAMED_ACTORS);
+        }
+
+        Uri actorUri = Uri.parse(actorUriString);
+
+        String actorScheme = actorUri.getScheme();
+        List<String> actorPathSegments = actorUri.getPathSegments();
+        if (!"overlay".equals(actorScheme) || CollectionUtils.size(actorPathSegments) != 1) {
+            return Pair.create(null, ActorState.INVALID_OVERLAYABLE_ACTOR_NAME);
+        }
+
+        String actorNamespace = actorUri.getAuthority();
+        Map<String, String> namespace = namedActors.get(actorNamespace);
+        if (namespace == null) {
+            return Pair.create(null, ActorState.MISSING_NAMESPACE);
+        }
+
+        String actorName = actorPathSegments.get(0);
+        String packageName = namespace.get(actorName);
+        if (TextUtils.isEmpty(packageName)) {
+            return Pair.create(null, ActorState.MISSING_ACTOR_NAME);
+        }
+
+        return Pair.create(packageName, ActorState.ALLOWED);
+    }
+
     public OverlayActorEnforcer(@NonNull VerifyCallback verifyCallback) {
         mVerifyCallback = verifyCallback;
     }
@@ -141,31 +174,14 @@
             }
         }
 
-        Map<String, ? extends Map<String, String>> namedActors = mVerifyCallback.getNamedActors();
-        if (namedActors.isEmpty()) {
-            return ActorState.NO_NAMED_ACTORS;
+        Map<String, Map<String, String>> namedActors = mVerifyCallback.getNamedActors();
+        Pair<String, ActorState> actorUriPair = getPackageNameForActor(actor, namedActors);
+        ActorState actorUriState = actorUriPair.second;
+        if (actorUriState != ActorState.ALLOWED) {
+            return actorUriState;
         }
 
-        Uri actorUri = Uri.parse(actor);
-
-        String actorScheme = actorUri.getScheme();
-        List<String> actorPathSegments = actorUri.getPathSegments();
-        if (!"overlay".equals(actorScheme) || CollectionUtils.size(actorPathSegments) != 1) {
-            return ActorState.INVALID_OVERLAYABLE_ACTOR_NAME;
-        }
-
-        String actorNamespace = actorUri.getAuthority();
-        Map<String, String> namespace = namedActors.get(actorNamespace);
-        if (namespace == null) {
-            return ActorState.MISSING_NAMESPACE;
-        }
-
-        String actorName = actorPathSegments.get(0);
-        String packageName = namespace.get(actorName);
-        if (TextUtils.isEmpty(packageName)) {
-            return ActorState.MISSING_ACTOR_NAME;
-        }
-
+        String packageName = actorUriPair.first;
         PackageInfo packageInfo = mVerifyCallback.getPackageInfo(packageName, userId);
         if (packageInfo == null) {
             return ActorState.MISSING_APP_INFO;
@@ -192,7 +208,7 @@
      * For easier logging/debugging, a set of all possible failure/success states when running
      * enforcement.
      */
-    private enum ActorState {
+    enum ActorState {
         ALLOWED,
         INVALID_ACTOR,
         MISSING_NAMESPACE,
@@ -244,7 +260,7 @@
          * value maps actor name to package name
          */
         @NonNull
-        Map<String, ? extends Map<String, String>> getNamedActors();
+        Map<String, Map<String, String>> getNamedActors();
 
         /**
          * @return true if the target package has declared an overlayable
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 8b69946..f1947ac 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -1073,8 +1073,6 @@
             mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         }
 
-        // TODO(b/143096091): Remove PackageInfo cache so that PackageManager is always queried
-        //  to enforce visibility/other permission checks
         public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId,
                 final boolean useCache) {
             if (useCache) {
@@ -1097,18 +1095,12 @@
 
         @Override
         public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId) {
-            // TODO(b/143096091): Remove clearing calling ID
-            long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                return getPackageInfo(packageName, userId, true);
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
-            }
+            return getPackageInfo(packageName, userId, true);
         }
 
         @NonNull
         @Override
-        public Map<String, ? extends Map<String, String>> getNamedActors() {
+        public Map<String, Map<String, String>> getNamedActors() {
             return SystemConfig.getInstance().getNamedActors();
         }
 
@@ -1136,57 +1128,45 @@
         public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
                 @Nullable String targetOverlayableName, int userId)
                 throws IOException {
-            // TODO(b/143096091): Remove clearing calling ID
-            long callingIdentity = Binder.clearCallingIdentity();
+            PackageInfo packageInfo = getPackageInfo(packageName, userId);
+            if (packageInfo == null) {
+                throw new IOException("Unable to get target package");
+            }
+
+            String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
+
+            ApkAssets apkAssets = null;
             try {
-                PackageInfo packageInfo = getPackageInfo(packageName, userId);
-                if (packageInfo == null) {
-                    throw new IOException("Unable to get target package");
-                }
-
-                String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
-
-                ApkAssets apkAssets = null;
-                try {
-                    apkAssets = ApkAssets.loadFromPath(baseCodePath);
-                    return apkAssets.getOverlayableInfo(targetOverlayableName);
-                } finally {
-                    if (apkAssets != null) {
-                        try {
-                            apkAssets.close();
-                        } catch (Throwable ignored) {
-                        }
+                apkAssets = ApkAssets.loadFromPath(baseCodePath);
+                return apkAssets.getOverlayableInfo(targetOverlayableName);
+            } finally {
+                if (apkAssets != null) {
+                    try {
+                        apkAssets.close();
+                    } catch (Throwable ignored) {
                     }
                 }
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
             }
         }
 
         @Override
         public boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
                 throws RemoteException, IOException {
-            // TODO(b/143096091): Remove clearing calling ID
-            long callingIdentity = Binder.clearCallingIdentity();
-            try {
-                PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, 0,
-                        userId);
-                String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
+            PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, 0,
+                    userId);
+            String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
 
-                ApkAssets apkAssets = null;
-                try {
-                    apkAssets = ApkAssets.loadFromPath(baseCodePath);
-                    return apkAssets.definesOverlayable();
-                } finally {
-                    if (apkAssets != null) {
-                        try {
-                            apkAssets.close();
-                        } catch (Throwable ignored) {
-                        }
+            ApkAssets apkAssets = null;
+            try {
+                apkAssets = ApkAssets.loadFromPath(baseCodePath);
+                return apkAssets.definesOverlayable();
+            } finally {
+                if (apkAssets != null) {
+                    try {
+                        apkAssets.close();
+                    } catch (Throwable ignored) {
                     }
                 }
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
             }
         }
 
@@ -1229,16 +1209,10 @@
         @Nullable
         @Override
         public String[] getPackagesForUid(int uid) {
-            // TODO(b/143096091): Remove clearing calling ID
-            long callingIdentity = Binder.clearCallingIdentity();
             try {
-                try {
-                    return mPackageManager.getPackagesForUid(uid);
-                } catch (RemoteException ignored) {
-                    return null;
-                }
-            } finally {
-                Binder.restoreCallingIdentity(callingIdentity);
+                return mPackageManager.getPackagesForUid(uid);
+            } catch (RemoteException ignored) {
+                return null;
             }
         }
 
diff --git a/services/core/java/com/android/server/om/OverlayReferenceMapper.java b/services/core/java/com/android/server/om/OverlayReferenceMapper.java
new file mode 100644
index 0000000..8bea119
--- /dev/null
+++ b/services/core/java/com/android/server/om/OverlayReferenceMapper.java
@@ -0,0 +1,375 @@
+/*
+ * 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.om;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.parsing.AndroidPackage;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Pair;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.CollectionUtils;
+import com.android.server.SystemConfig;
+import com.android.server.pm.PackageSetting;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Track visibility of a targets and overlays to actors.
+ *
+ * 4 cases to handle:
+ * <ol>
+ *     <li>Target adds/changes an overlayable to add a reference to an actor
+ *         <ul>
+ *             <li>Must expose target to actor</li>
+ *             <li>Must expose any overlays that pointed to that overlayable name to the actor</li>
+ *         </ul>
+ *     </li>
+ *     <li>Target removes/changes an overlayable to remove a reference to an actor
+ *         <ul>
+ *             <li>If this target has no other overlayables referencing the actor, hide the
+ *             target</li>
+ *             <li>For all overlays targeting this overlayable, if the overlay is only visible to
+ *             the actor through this overlayable, hide the overlay</li>
+ *         </ul>
+ *     </li>
+ *     <li>Overlay adds/changes an overlay tag to add a reference to an overlayable name
+ *         <ul>
+ *             <li>Expose this overlay to the actor defined by the target overlayable</li>
+ *         </ul>
+ *     </li>
+ *     <li>Overlay removes/changes an overlay tag to remove a reference to an overlayable name
+ *         <ul>
+ *             <li>If this overlay is only visible to an actor through this overlayable name's
+ *             target's actor</li>
+ *         </ul>
+ *     </li>
+ * </ol>
+ *
+ * In this class, the names "actor", "target", and "overlay" all refer to the ID representations.
+ * All other use cases are named appropriate. "actor" is actor name, "target" is target package
+ * name, and "overlay" is overlay package name.
+ */
+public class OverlayReferenceMapper {
+
+    private final Object mLock = new Object();
+
+    /**
+     * Keys are actors, values are maps which map target to a set of overlays targeting it.
+     * The presence of a target in the value map means the actor and targets are connected, even
+     * if the corresponding target's set is empty.
+     * See class comment for specific types.
+     */
+    @GuardedBy("mLock")
+    private final Map<String, Map<String, Set<String>>> mActorToTargetToOverlays = new HashMap<>();
+
+    /**
+     * Keys are actor package names, values are generic package names the actor should be able
+     * to see.
+     */
+    @GuardedBy("mLock")
+    private final Map<String, Set<String>> mActorPkgToPkgs = new HashMap<>();
+
+    @GuardedBy("mLock")
+    private boolean mDeferRebuild;
+
+    @NonNull
+    private final Provider mProvider;
+
+    /**
+     * @param deferRebuild whether or not to defer rebuild calls on add/remove until first get call;
+     *                     useful during boot when multiple packages are added in rapid succession
+     *                     and queries in-between are not expected
+     */
+    public OverlayReferenceMapper(boolean deferRebuild, @Nullable Provider provider) {
+        this.mDeferRebuild = deferRebuild;
+        this.mProvider = provider != null ? provider : new Provider() {
+            @Nullable
+            @Override
+            public String getActorPkg(String actor) {
+                Map<String, Map<String, String>> namedActors = SystemConfig.getInstance()
+                        .getNamedActors();
+
+                Pair<String, OverlayActorEnforcer.ActorState> actorPair =
+                        OverlayActorEnforcer.getPackageNameForActor(actor, namedActors);
+                return actorPair.first;
+            }
+
+            @NonNull
+            @Override
+            public Map<String, Set<String>> getTargetToOverlayables(@NonNull AndroidPackage pkg) {
+                String target = pkg.getOverlayTarget();
+                if (TextUtils.isEmpty(target)) {
+                    return Collections.emptyMap();
+                }
+
+                String overlayable = pkg.getOverlayTargetName();
+                Map<String, Set<String>> targetToOverlayables = new HashMap<>();
+                Set<String> overlayables = new HashSet<>();
+                overlayables.add(overlayable);
+                targetToOverlayables.put(target, overlayables);
+                return targetToOverlayables;
+            }
+        };
+    }
+
+    /**
+     * @return mapping of actor package to a set of packages it can view
+     */
+    @NonNull
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public Map<String, Set<String>> getActorPkgToPkgs() {
+        return mActorPkgToPkgs;
+    }
+
+    public boolean isValidActor(@NonNull String targetName, @NonNull String actorPackageName) {
+        synchronized (mLock) {
+            assertMapBuilt();
+            Set<String> validSet = mActorPkgToPkgs.get(actorPackageName);
+            return validSet != null && validSet.contains(targetName);
+        }
+    }
+
+    /**
+     * Add a package to be considered for visibility. Currently supports adding as a target and/or
+     * an overlay. Adding an actor is not supported. Those are configured as part of
+     * {@link SystemConfig#getNamedActors()}.
+     *
+     * @param pkg the package to add
+     * @param otherPkgs map of other packages to consider, excluding {@param pkg}
+     */
+    public void addPkg(AndroidPackage pkg, Map<String, AndroidPackage> otherPkgs) {
+        synchronized (mLock) {
+            if (!pkg.getOverlayables().isEmpty()) {
+                addTarget(pkg, otherPkgs);
+            }
+
+            // TODO(b/135203078): Replace with isOverlay boolean flag check; fix test mocks
+            if (!mProvider.getTargetToOverlayables(pkg).isEmpty()) {
+                addOverlay(pkg, otherPkgs);
+            }
+
+            if (!mDeferRebuild) {
+                rebuild();
+            }
+        }
+    }
+
+    /**
+     * Removes a package to be considered for visibility. Currently supports removing as a target
+     * and/or an overlay. Removing an actor is not supported. Those are staticly configured as part
+     * of {@link SystemConfig#getNamedActors()}.
+     *
+     * @param pkgName name to remove, as was added through {@link #addPkg(AndroidPackage, Map)}
+     */
+    public void removePkg(String pkgName) {
+        synchronized (mLock) {
+            removeTarget(pkgName);
+            removeOverlay(pkgName);
+
+            if (!mDeferRebuild) {
+                rebuild();
+            }
+        }
+    }
+
+    private void removeTarget(String target) {
+        synchronized (mLock) {
+            Iterator<Map<String, Set<String>>> iterator =
+                    mActorToTargetToOverlays.values().iterator();
+            while (iterator.hasNext()) {
+                Map<String, Set<String>> next = iterator.next();
+                next.remove(target);
+                if (next.isEmpty()) {
+                    iterator.remove();
+                }
+            }
+        }
+    }
+
+    /**
+     * Associate an actor with an association of a new target to overlays for that target.
+     *
+     * If a target overlays itself, it will not be associated with itself, as only one half of the
+     * relationship needs to exist for visibility purposes.
+     */
+    private void addTarget(AndroidPackage targetPkg, Map<String, AndroidPackage> otherPkgs) {
+        synchronized (mLock) {
+            String target = targetPkg.getPackageName();
+            removeTarget(target);
+
+            Map<String, String> overlayablesToActors = targetPkg.getOverlayables();
+            for (String overlayable : overlayablesToActors.keySet()) {
+                String actor = overlayablesToActors.get(overlayable);
+                addTargetToMap(actor, target);
+
+                for (AndroidPackage overlayPkg : otherPkgs.values()) {
+                    Map<String, Set<String>> targetToOverlayables =
+                            mProvider.getTargetToOverlayables(overlayPkg);
+                    Set<String> overlayables = targetToOverlayables.get(target);
+                    if (CollectionUtils.isEmpty(overlayables)) {
+                        continue;
+                    }
+
+                    if (overlayables.contains(overlayable)) {
+                        addOverlayToMap(actor, target, overlayPkg.getPackageName());
+                    }
+                }
+            }
+        }
+    }
+
+    private void removeOverlay(String overlay) {
+        synchronized (mLock) {
+            for (Map<String, Set<String>> targetToOverlays : mActorToTargetToOverlays.values()) {
+                for (Set<String> overlays : targetToOverlays.values()) {
+                    overlays.remove(overlay);
+                }
+            }
+        }
+    }
+
+    /**
+     * Associate an actor with an association of targets to overlays for a new overlay.
+     *
+     * If an overlay targets itself, it will not be associated with itself, as only one half of the
+     * relationship needs to exist for visibility purposes.
+     */
+    private void addOverlay(AndroidPackage overlayPkg, Map<String, AndroidPackage> otherPkgs) {
+        synchronized (mLock) {
+            String overlay = overlayPkg.getPackageName();
+            removeOverlay(overlay);
+
+            Map<String, Set<String>> targetToOverlayables =
+                    mProvider.getTargetToOverlayables(overlayPkg);
+            for (Map.Entry<String, Set<String>> entry : targetToOverlayables.entrySet()) {
+                String target = entry.getKey();
+                Set<String> overlayables = entry.getValue();
+                AndroidPackage targetPkg = otherPkgs.get(target);
+                if (targetPkg == null) {
+                    continue;
+                }
+
+                String targetPkgName = targetPkg.getPackageName();
+                Map<String, String> overlayableToActor = targetPkg.getOverlayables();
+                for (String overlayable : overlayables) {
+                    String actor = overlayableToActor.get(overlayable);
+                    if (TextUtils.isEmpty(actor)) {
+                        continue;
+                    }
+                    addOverlayToMap(actor, targetPkgName, overlay);
+                }
+            }
+        }
+    }
+
+    public void rebuildIfDeferred() {
+        synchronized (mLock) {
+            if (mDeferRebuild) {
+                rebuild();
+                mDeferRebuild = false;
+            }
+        }
+    }
+
+    private void assertMapBuilt() {
+        if (mDeferRebuild) {
+            throw new IllegalStateException("The actor map must be built by calling "
+                    + "rebuildIfDeferred before it is queried");
+        }
+    }
+
+    private void rebuild() {
+        synchronized (mLock) {
+            mActorPkgToPkgs.clear();
+            for (String actor : mActorToTargetToOverlays.keySet()) {
+                String actorPkg = mProvider.getActorPkg(actor);
+                if (TextUtils.isEmpty(actorPkg)) {
+                    continue;
+                }
+
+                Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
+                Set<String> pkgs = new HashSet<>();
+
+                for (String target : targetToOverlays.keySet()) {
+                    Set<String> overlays = targetToOverlays.get(target);
+                    pkgs.add(target);
+                    pkgs.addAll(overlays);
+                }
+
+                mActorPkgToPkgs.put(actorPkg, pkgs);
+            }
+        }
+    }
+
+    private void addTargetToMap(String actor, String target) {
+        Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
+        if (targetToOverlays == null) {
+            targetToOverlays = new HashMap<>();
+            mActorToTargetToOverlays.put(actor, targetToOverlays);
+        }
+
+        Set<String> overlays = targetToOverlays.get(target);
+        if (overlays == null) {
+            overlays = new HashSet<>();
+            targetToOverlays.put(target, overlays);
+        }
+    }
+
+    private void addOverlayToMap(String actor, String target, String overlay) {
+        synchronized (mLock) {
+            Map<String, Set<String>> targetToOverlays = mActorToTargetToOverlays.get(actor);
+            if (targetToOverlays == null) {
+                targetToOverlays = new HashMap<>();
+                mActorToTargetToOverlays.put(actor, targetToOverlays);
+            }
+
+            Set<String> overlays = targetToOverlays.get(target);
+            if (overlays == null) {
+                overlays = new HashSet<>();
+                targetToOverlays.put(target, overlays);
+            }
+
+            overlays.add(overlay);
+        }
+    }
+
+    public interface Provider {
+
+        /**
+         * Given the actor string from an overlayable definition, return the actor's package name.
+         */
+        @Nullable
+        String getActorPkg(String actor);
+
+        /**
+         * Mock response of multiple overlay tags.
+         *
+         * TODO(b/119899133): Replace with actual implementation; fix OverlayReferenceMapperTests
+         */
+        @NonNull
+        Map<String, Set<String>> getTargetToOverlayables(@NonNull AndroidPackage pkg);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 8374ee6..c4bcf80 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -16,8 +16,6 @@
 
 package com.android.server.pm;
 
-import static android.content.pm.PackageParser.Component;
-import static android.content.pm.PackageParser.IntentInfo;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
 
@@ -26,7 +24,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.content.pm.parsing.AndroidPackage;
 import android.content.pm.parsing.ComponentParseUtils;
 import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
@@ -50,9 +47,9 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.FgThread;
+import com.android.server.om.OverlayReferenceMapper;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -109,11 +106,16 @@
 
     private final FeatureConfig mFeatureConfig;
 
+    private final OverlayReferenceMapper mOverlayReferenceMapper;
+
     AppsFilter(FeatureConfig featureConfig, String[] forceQueryableWhitelist,
-            boolean systemAppsQueryable) {
+            boolean systemAppsQueryable,
+            @Nullable OverlayReferenceMapper.Provider overlayProvider) {
         mFeatureConfig = featureConfig;
         mForceQueryableByDevicePackageNames = forceQueryableWhitelist;
         mSystemAppsQueryable = systemAppsQueryable;
+        mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/,
+                overlayProvider);
     }
 
     public interface FeatureConfig {
@@ -193,7 +195,7 @@
             }
         }
         return new AppsFilter(featureConfig, forcedQueryablePackageNames,
-                forceSystemAppsQueryable);
+                forceSystemAppsQueryable, null);
     }
 
     /** Returns true if the querying package may query for the potential target package */
@@ -282,6 +284,7 @@
 
     public void onSystemReady() {
         mFeatureConfig.onSystemReady();
+        mOverlayReferenceMapper.rebuildIfDeferred();
     }
 
     /**
@@ -338,6 +341,16 @@
                     }
                 }
             }
+
+            int existingSize = existingSettings.size();
+            ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize);
+            for (int index = 0; index < existingSize; index++) {
+                PackageSetting pkgSetting = existingSettings.valueAt(index);
+                if (pkgSetting.pkg != null) {
+                    existingPkgs.put(pkgSetting.name, pkgSetting.pkg);
+                }
+            }
+            mOverlayReferenceMapper.addPkg(newPkgSetting.pkg, existingPkgs);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -381,6 +394,8 @@
                 addPackage(setting.sharedUser.packages.valueAt(i), existingSettings);
             }
         }
+
+        mOverlayReferenceMapper.removePkg(setting.name);
     }
 
     /**
@@ -397,8 +412,7 @@
             PackageSetting targetPkgSetting, int userId) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplication");
         try {
-            if (!shouldFilterApplicationInternal(callingUid, callingSetting,
-                    targetPkgSetting,
+            if (!shouldFilterApplicationInternal(callingUid, callingSetting, targetPkgSetting,
                     userId)) {
                 return false;
             }
@@ -412,8 +426,8 @@
         }
     }
 
-    private boolean shouldFilterApplicationInternal(int callingUid,
-            SettingBase callingSetting, PackageSetting targetPkgSetting, int userId) {
+    private boolean shouldFilterApplicationInternal(int callingUid, SettingBase callingSetting,
+            PackageSetting targetPkgSetting, int userId) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "shouldFilterApplicationInternal");
         try {
             final boolean featureEnabled = mFeatureConfig.isGloballyEnabled();
@@ -530,6 +544,29 @@
                     }
                 }
             }
+
+            if (callingSharedPkgSettings != null) {
+                int size = callingSharedPkgSettings.size();
+                for (int index = 0; index < size; index++) {
+                    PackageSetting pkgSetting = callingSharedPkgSettings.valueAt(index);
+                    if (mOverlayReferenceMapper.isValidActor(targetName, pkgSetting.name)) {
+                        if (DEBUG_LOGGING) {
+                            log(callingPkgSetting, targetPkgSetting,
+                                    "matches shared user of package that acts on target of "
+                                            + "overlay");
+                        }
+                        return false;
+                    }
+                }
+            } else {
+                if (mOverlayReferenceMapper.isValidActor(targetName, callingPkgSetting.name)) {
+                    if (DEBUG_LOGGING) {
+                        log(callingPkgSetting, targetPkgSetting, "acts on target of overlay");
+                    }
+                    return false;
+                }
+            }
+
             return true;
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 30b2c9d..8333ae5 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -39,6 +39,7 @@
 import android.content.pm.AuxiliaryResolveInfo;
 import android.content.pm.InstantAppIntentFilter;
 import android.content.pm.InstantAppRequest;
+import android.content.pm.InstantAppRequestInfo;
 import android.content.pm.InstantAppResolveInfo;
 import android.content.pm.InstantAppResolveInfo.InstantAppDigest;
 import android.metrics.LogMaker;
@@ -47,6 +48,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 
@@ -63,7 +66,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-import java.util.UUID;
 
 /** @hide */
 public abstract class InstantAppResolver {
@@ -117,26 +119,40 @@
         return sanitizedIntent;
     }
 
+    /**
+     * Generate an {@link InstantAppDigest} from an {@link Intent} which contains hashes of the
+     * host. The object contains both secure and insecure hash array variants, and the secure
+     * version must be passed along to ensure the random data is consistent.
+     */
+    @NonNull
+    public static InstantAppDigest parseDigest(@NonNull Intent origIntent) {
+        if (origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost())) {
+            return new InstantAppResolveInfo.InstantAppDigest(origIntent.getData().getHost(),
+                    5 /*maxDigests*/);
+        } else {
+            return InstantAppResolveInfo.InstantAppDigest.UNDEFINED;
+        }
+    }
+
     public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(
             InstantAppResolverConnection connection, InstantAppRequest requestObj) {
         final long startTime = System.currentTimeMillis();
-        final String token = UUID.randomUUID().toString();
+        final String token = requestObj.token;
         if (DEBUG_INSTANT) {
             Log.d(TAG, "[" + token + "] Phase1; resolving");
         }
-        final Intent origIntent = requestObj.origIntent;
-        final Intent sanitizedIntent = sanitizeIntent(origIntent);
 
         AuxiliaryResolveInfo resolveInfo = null;
         @ResolutionStatus int resolutionStatus = RESOLUTION_SUCCESS;
+        Intent origIntent = requestObj.origIntent;
         try {
             final List<InstantAppResolveInfo> instantAppResolveInfoList =
-                    connection.getInstantAppResolveInfoList(sanitizedIntent,
-                            requestObj.digest.getDigestPrefixSecure(), requestObj.userId, token);
+                    connection.getInstantAppResolveInfoList(buildRequestInfo(requestObj));
             if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
                 resolveInfo = InstantAppResolver.filterInstantAppIntent(
                         instantAppResolveInfoList, origIntent, requestObj.resolvedType,
-                        requestObj.userId, origIntent.getPackage(), requestObj.digest, token);
+                        requestObj.userId, origIntent.getPackage(), token,
+                        requestObj.hostDigestPrefixSecure);
             }
         } catch (ConnectionException e) {
             if (e.failure == ConnectionException.FAILURE_BIND) {
@@ -166,7 +182,7 @@
         // if the match external flag is set, return an empty resolve info instead of a null result.
         if (resolveInfo == null && (origIntent.getFlags() & FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
             return new AuxiliaryResolveInfo(token, false, createFailureIntent(origIntent, token),
-                    null /* filters */);
+                    null /* filters */, requestObj.hostDigestPrefixSecure);
         }
         return resolveInfo;
     }
@@ -175,7 +191,7 @@
             InstantAppResolverConnection connection, InstantAppRequest requestObj,
             ActivityInfo instantAppInstaller, Handler callbackHandler) {
         final long startTime = System.currentTimeMillis();
-        final String token = requestObj.responseObj.token;
+        final String token = requestObj.token;
         if (DEBUG_INSTANT) {
             Log.d(TAG, "[" + token + "] Phase2; resolving");
         }
@@ -191,8 +207,8 @@
                     final AuxiliaryResolveInfo instantAppIntentInfo =
                             InstantAppResolver.filterInstantAppIntent(
                                     instantAppResolveInfoList, origIntent, null /*resolvedType*/,
-                                    0 /*userId*/, origIntent.getPackage(), requestObj.digest,
-                                    token);
+                                    0 /*userId*/, origIntent.getPackage(),
+                                    token, requestObj.hostDigestPrefixSecure);
                     if (instantAppIntentInfo != null) {
                         failureIntent = instantAppIntentInfo.failureIntent;
                     } else {
@@ -223,8 +239,7 @@
             }
         };
         try {
-            connection.getInstantAppIntentFilterList(sanitizedIntent,
-                    requestObj.digest.getDigestPrefixSecure(), requestObj.userId, token, callback,
+            connection.getInstantAppIntentFilterList(buildRequestInfo(requestObj), callback,
                     callbackHandler, startTime);
         } catch (ConnectionException e) {
             @ResolutionStatus int resolutionStatus = RESOLUTION_FAILURE;
@@ -356,10 +371,22 @@
         return intent;
     }
 
+    private static InstantAppRequestInfo buildRequestInfo(InstantAppRequest request) {
+        return new InstantAppRequestInfo(
+                sanitizeIntent(request.origIntent),
+                // This must only expose the secured version of the host
+                request.hostDigestPrefixSecure,
+                UserHandle.getUserHandleForUid(request.userId),
+                request.isRequesterInstantApp,
+                request.token
+        );
+    }
+
     private static AuxiliaryResolveInfo filterInstantAppIntent(
-            List<InstantAppResolveInfo> instantAppResolveInfoList,
-            Intent origIntent, String resolvedType, int userId, String packageName,
-            InstantAppDigest digest, String token) {
+            List<InstantAppResolveInfo> instantAppResolveInfoList, Intent origIntent,
+            String resolvedType, int userId, String packageName, String token,
+            int[] hostDigestPrefixSecure) {
+        InstantAppDigest digest = InstantAppResolver.parseDigest(origIntent);
         final int[] shaPrefix = digest.getDigestPrefix();
         final byte[][] digestBytes = digest.getDigestBytes();
         boolean requiresSecondPhase = false;
@@ -404,7 +431,7 @@
         }
         if (filters != null && !filters.isEmpty()) {
             return new AuxiliaryResolveInfo(token, requiresSecondPhase,
-                    createFailureIntent(origIntent, token), filters);
+                    createFailureIntent(origIntent, token), filters, hostDigestPrefixSecure);
         }
         // Hash or filter mis-match; no instant apps for this domain.
         return null;
diff --git a/services/core/java/com/android/server/pm/InstantAppResolverConnection.java b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
index c0e9f58..0fe2eb1 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.InstantAppRequestInfo;
 import android.content.pm.InstantAppResolveInfo;
 import android.os.Binder;
 import android.os.Build;
@@ -85,13 +86,13 @@
         mBgHandler = BackgroundThread.getHandler();
     }
 
-    public List<InstantAppResolveInfo> getInstantAppResolveInfoList(Intent sanitizedIntent,
-            int[] hashPrefix, int userId, String token) throws ConnectionException {
+    public List<InstantAppResolveInfo> getInstantAppResolveInfoList(InstantAppRequestInfo request)
+            throws ConnectionException {
         throwIfCalledOnMainThread();
         IInstantAppResolver target = null;
         try {
             try {
-                target = getRemoteInstanceLazy(token);
+                target = getRemoteInstanceLazy(request.token);
             } catch (TimeoutException e) {
                 throw new ConnectionException(ConnectionException.FAILURE_BIND);
             } catch (InterruptedException e) {
@@ -99,8 +100,7 @@
             }
             try {
                 return mGetInstantAppResolveInfoCaller
-                        .getInstantAppResolveInfoList(target, sanitizedIntent, hashPrefix, userId,
-                                token);
+                        .getInstantAppResolveInfoList(target, request);
             } catch (TimeoutException e) {
                 throw new ConnectionException(ConnectionException.FAILURE_CALL);
             } catch (RemoteException ignore) {
@@ -113,8 +113,8 @@
         return null;
     }
 
-    public void getInstantAppIntentFilterList(Intent sanitizedIntent, int[] hashPrefix, int userId,
-            String token, PhaseTwoCallback callback, Handler callbackHandler, final long startTime)
+    public void getInstantAppIntentFilterList(InstantAppRequestInfo request,
+            PhaseTwoCallback callback, Handler callbackHandler, final long startTime)
             throws ConnectionException {
         final IRemoteCallback remoteCallback = new IRemoteCallback.Stub() {
             @Override
@@ -126,9 +126,8 @@
             }
         };
         try {
-            getRemoteInstanceLazy(token)
-                    .getInstantAppIntentFilterList(sanitizedIntent, hashPrefix, userId, token,
-                            remoteCallback);
+            getRemoteInstanceLazy(request.token)
+                    .getInstantAppIntentFilterList(request, remoteCallback);
         } catch (TimeoutException e) {
             throw new ConnectionException(ConnectionException.FAILURE_BIND);
         } catch (InterruptedException e) {
@@ -352,12 +351,10 @@
             };
         }
 
-        public List<InstantAppResolveInfo> getInstantAppResolveInfoList(
-                IInstantAppResolver target, Intent sanitizedIntent, int[] hashPrefix, int userId,
-                String token) throws RemoteException, TimeoutException {
+        public List<InstantAppResolveInfo> getInstantAppResolveInfoList(IInstantAppResolver target,
+                InstantAppRequestInfo request) throws RemoteException, TimeoutException {
             final int sequence = onBeforeRemoteCall();
-            target.getInstantAppResolveInfoList(sanitizedIntent, hashPrefix, userId, token,
-                    sequence, mCallback);
+            target.getInstantAppResolveInfoList(request, sequence, mCallback);
             return getResultTimed(sequence);
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index dceca0a..a54534b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -637,7 +637,7 @@
         session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
                 mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid,
                 installSource, params, createdMillis,
-                stageDir, stageCid, false, false, false, null, SessionInfo.INVALID_ID,
+                stageDir, stageCid, null, false, false, false, null, SessionInfo.INVALID_ID,
                 false, false, false, SessionInfo.STAGED_SESSION_NO_ERROR, "");
 
         synchronized (mSessions) {
@@ -1014,12 +1014,28 @@
         }
     }
 
+    static void sendPendingStreaming(Context context, IntentSender target, int sessionId,
+            Throwable cause) {
+        final Intent intent = new Intent();
+        intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+        intent.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_STREAMING);
+        if (cause != null && !TextUtils.isEmpty(cause.getMessage())) {
+            intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
+                    "Staging Image Not Ready [" + cause.getMessage() + "]");
+        } else {
+            intent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, "Staging Image Not Ready");
+        }
+        try {
+            target.sendIntent(context, 0, intent, null, null);
+        } catch (SendIntentException ignored) {
+        }
+    }
+
     static void sendOnUserActionRequired(Context context, IntentSender target, int sessionId,
             Intent intent) {
         final Intent fillIn = new Intent();
         fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
-        fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
-                PackageInstaller.STATUS_PENDING_USER_ACTION);
+        fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION);
         fillIn.putExtra(Intent.EXTRA_INTENT, intent);
         try {
             target.sendIntent(context, 0, fillIn, null, null);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index d6ca86a..286d291 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -21,6 +21,7 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
 import static android.content.pm.PackageParser.APEX_FILE_EXTENSION;
 import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
@@ -30,11 +31,13 @@
 
 import static com.android.internal.util.XmlUtils.readBitmapAttribute;
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readByteArrayAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readLongAttribute;
 import static com.android.internal.util.XmlUtils.readStringAttribute;
 import static com.android.internal.util.XmlUtils.readUriAttribute;
 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeByteArrayAttribute;
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
@@ -123,6 +126,7 @@
 import java.io.FileFilter;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedList;
@@ -142,6 +146,7 @@
     /** XML constants used for persisting a session */
     static final String TAG_SESSION = "session";
     static final String TAG_CHILD_SESSION = "childSession";
+    static final String TAG_SESSION_FILE = "sessionFile";
     private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
     private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION =
             "whitelisted-restricted-permission";
@@ -183,6 +188,9 @@
     private static final String ATTR_VOLUME_UUID = "volumeUuid";
     private static final String ATTR_NAME = "name";
     private static final String ATTR_INSTALL_REASON = "installRason";
+    private static final String ATTR_DATA_LOADER_PACKAGE_NAME = "dataLoaderPackageName";
+    private static final String ATTR_LENGTH_BYTES = "lengthBytes";
+    private static final String ATTR_METADATA = "metadata";
 
     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
     private static final int[] EMPTY_CHILD_SESSION_ARRAY = {};
@@ -278,6 +286,29 @@
     @GuardedBy("mLock")
     private int mParentSessionId;
 
+    static class FileInfo {
+        public final String name;
+        public final Long lengthBytes;
+        public final byte[] metadata;
+
+        public static FileInfo added(String name, Long lengthBytes, byte[] metadata) {
+            return new FileInfo(name, lengthBytes, metadata);
+        }
+
+        public static FileInfo removed(String name) {
+            return new FileInfo(name, -1L, null);
+        }
+
+        FileInfo(String name, Long lengthBytes, byte[] metadata) {
+            this.name = name;
+            this.lengthBytes = lengthBytes;
+            this.metadata = metadata;
+        }
+    }
+
+    @GuardedBy("mLock")
+    private ArrayList<FileInfo> mFiles = new ArrayList<>();
+
     @GuardedBy("mLock")
     private boolean mStagedSessionApplied;
     @GuardedBy("mLock")
@@ -313,6 +344,7 @@
     @GuardedBy("mLock")
     private boolean mVerityFound;
 
+    // TODO(b/146080380): merge file list with Callback installation.
     private IncrementalFileStorages mIncrementalFileStorages;
 
     private static final FileFilter sAddedFilter = new FileFilter() {
@@ -344,7 +376,9 @@
             IntentSender statusReceiver;
             switch (msg.what) {
                 case MSG_SEAL:
-                    handleSeal((IntentSender) msg.obj);
+                    statusReceiver = (IntentSender) msg.obj;
+
+                    handleSeal(statusReceiver);
                     break;
                 case MSG_COMMIT:
                     handleCommit();
@@ -378,6 +412,10 @@
         }
     };
 
+    private boolean isDataLoaderInstallation() {
+        return !TextUtils.isEmpty(params.dataLoaderPackageName);
+    }
+
     /**
      * @return {@code true} iff the installing is app an device owner or affiliated profile owner.
      */
@@ -435,7 +473,8 @@
             PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager,
             int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
             SessionParams params, long createdMillis,
-            File stageDir, String stageCid, boolean prepared, boolean committed, boolean sealed,
+            File stageDir, String stageCid, FileInfo[] files, boolean prepared,
+            boolean committed, boolean sealed,
             @Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
             boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
             String stagedSessionErrorMessage) {
@@ -464,6 +503,12 @@
         }
         this.mParentSessionId = parentSessionId;
 
+        if (files != null) {
+            for (FileInfo file : files) {
+                mFiles.add(file);
+            }
+        }
+
         if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) {
             throw new IllegalArgumentException(
                     "Exactly one of stageDir or stageCid stage must be set");
@@ -592,15 +637,19 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void setClientProgressLocked(float progress) {
+        // Always publish first staging movement
+        final boolean forcePublish = (mClientProgress == 0);
+        mClientProgress = progress;
+        computeProgressLocked(forcePublish);
+    }
+
     @Override
     public void setClientProgress(float progress) {
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
-
-            // Always publish first staging movement
-            final boolean forcePublish = (mClientProgress == 0);
-            mClientProgress = progress;
-            computeProgressLocked(forcePublish);
+            setClientProgressLocked(progress);
         }
     }
 
@@ -608,8 +657,7 @@
     public void addClientProgress(float progress) {
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
-
-            setClientProgress(mClientProgress + progress);
+            setClientProgressLocked(mClientProgress + progress);
         }
     }
 
@@ -637,7 +685,10 @@
 
     @GuardedBy("mLock")
     private String[] getNamesLocked() {
-        return stageDir.list();
+        if (!isDataLoaderInstallation()) {
+            return stageDir.list();
+        }
+        return mFiles.stream().map(fileInfo -> fileInfo.name).toArray(String[]::new);
     }
 
     private static File[] filterFiles(File parent, String[] names, FileFilter filter) {
@@ -659,6 +710,10 @@
 
     @Override
     public void removeSplit(String splitName) {
+        if (isDataLoaderInstallation()) {
+            throw new IllegalStateException(
+                    "Cannot remove splits in a callback installation session.");
+        }
         if (TextUtils.isEmpty(params.appPackageName)) {
             throw new IllegalStateException("Must specify package name to remove a split");
         }
@@ -693,8 +748,31 @@
         }
     }
 
+    private void assertCanWrite(boolean reverseMode) {
+        if (isDataLoaderInstallation()) {
+            throw new IllegalStateException(
+                    "Cannot write regular files in a callback installation session.");
+        }
+        synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+            assertPreparedAndNotSealedLocked("assertCanWrite");
+        }
+        if (reverseMode) {
+            switch (Binder.getCallingUid()) {
+                case android.os.Process.SHELL_UID:
+                case android.os.Process.ROOT_UID:
+                case android.os.Process.SYSTEM_UID:
+                    break;
+                default:
+                    throw new SecurityException(
+                            "Reverse mode only supported from shell or system");
+            }
+        }
+    }
+
     @Override
     public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
+        assertCanWrite(false);
         try {
             return doWriteInternal(name, offsetBytes, lengthBytes, null);
         } catch (IOException e) {
@@ -705,6 +783,7 @@
     @Override
     public void write(String name, long offsetBytes, long lengthBytes,
             ParcelFileDescriptor fd) {
+        assertCanWrite(fd != null);
         try {
             doWriteInternal(name, offsetBytes, lengthBytes, fd);
         } catch (IOException e) {
@@ -720,9 +799,6 @@
         final RevocableFileDescriptor fd;
         final FileBridge bridge;
         synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
-            assertPreparedAndNotSealedLocked("openWrite");
-
             if (PackageInstaller.ENABLE_REVOCABLE_FD) {
                 fd = new RevocableFileDescriptor();
                 bridge = null;
@@ -765,16 +841,6 @@
             }
 
             if (incomingFd != null) {
-                switch (Binder.getCallingUid()) {
-                    case android.os.Process.SHELL_UID:
-                    case android.os.Process.ROOT_UID:
-                    case android.os.Process.SYSTEM_UID:
-                        break;
-                    default:
-                        throw new SecurityException(
-                                "Reverse mode only supported from shell or system");
-                }
-
                 // In "reverse" mode, we're streaming data ourselves from the
                 // incoming FD, which means we never have to hand out our
                 // sensitive internal FD. We still rely on a "bridge" being
@@ -786,7 +852,10 @@
                                 if (params.sizeBytes > 0) {
                                     final long delta = progress - last.value;
                                     last.value = progress;
-                                    addClientProgress((float) delta / (float) params.sizeBytes);
+                                    synchronized (mLock) {
+                                        setClientProgressLocked(mClientProgress
+                                                + (float) delta / (float) params.sizeBytes);
+                                    }
                                 }
                             });
                 } finally {
@@ -821,6 +890,10 @@
 
     @Override
     public ParcelFileDescriptor openRead(String name) {
+        if (isDataLoaderInstallation()) {
+            throw new IllegalStateException(
+                    "Cannot read regular files in a callback installation session.");
+        }
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotCommittedOrDestroyedLocked("openRead");
@@ -926,6 +999,7 @@
                 return;
             }
         }
+
         mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
     }
 
@@ -988,6 +1062,14 @@
         }
     }
 
+    /** {@hide} */
+    private class StreamingException extends Exception {
+        StreamingException(Throwable cause) {
+            super(cause);
+        }
+    }
+
+
     /**
      * Sanity checks to make sure it's ok to commit the session.
      */
@@ -1039,12 +1121,22 @@
             }
 
             wasSealed = mSealed;
-            if (!mSealed) {
+            try {
+                if (!mSealed) {
+                    sealLocked(childSessions);
+                }
+
                 try {
-                    sealAndValidateLocked(childSessions);
-                } catch (PackageManagerException e) {
+                    streamAndValidateLocked();
+                } catch (StreamingException e) {
+                    // In case of streaming failure we don't want to fail or commit the session.
+                    // Just return from this method and allow caller to commit again.
+                    PackageInstallerService.sendPendingStreaming(mContext, mRemoteStatusReceiver,
+                            sessionId, e);
                     return false;
                 }
+            } catch (PackageManagerException e) {
+                return false;
             }
 
             // Client staging is fully done at this point
@@ -1131,17 +1223,26 @@
     }
 
     /**
-     * Seal the session to prevent further modification and validate the contents of it.
+     * Convenience wrapper, see {@link #sealLocked(List<PackageInstallerSession>) seal} and
+     * {@link #streamAndValidateLocked()}.
+     */
+    @GuardedBy("mLock")
+    private void sealAndValidateLocked(List<PackageInstallerSession> childSessions)
+            throws PackageManagerException, StreamingException {
+        sealLocked(childSessions);
+        streamAndValidateLocked();
+    }
+
+    /**
+     * Seal the session to prevent further modification.
      *
      * <p>The session will be sealed after calling this method even if it failed.
      *
-     * @param childSessions the child sessions of a multipackage that will be checked for
-     *                      consistency. Can be null if session is not multipackage.
      * @throws PackageManagerException if the session was sealed but something went wrong. If the
      *                                 session was sealed this is the only possible exception.
      */
     @GuardedBy("mLock")
-    private void sealAndValidateLocked(List<PackageInstallerSession> childSessions)
+    private void sealLocked(List<PackageInstallerSession> childSessions)
             throws PackageManagerException {
         try {
             assertNoWriteFileTransfersOpenLocked();
@@ -1152,7 +1253,26 @@
             if (childSessions != null) {
                 assertMultiPackageConsistencyLocked(childSessions);
             }
+        } catch (PackageManagerException e) {
+            throw onSessionVerificationFailure(e);
+        } catch (Throwable e) {
+            // Convert all exceptions into package manager exceptions as only those are handled
+            // in the code above.
+            throw onSessionVerificationFailure(new PackageManagerException(e));
+        }
+    }
 
+    /**
+     * Prepare DataLoader and stream content for DataLoader sessions.
+     * Validate the contents of all session.
+     *
+     * @throws StreamingException if streaming failed.
+     * @throws PackageManagerException if validation failed.
+     */
+    @GuardedBy("mLock")
+    private void streamAndValidateLocked()
+            throws PackageManagerException, StreamingException {
+        try {
             // Read transfers from the original owner stay open, but as the session's data cannot
             // be modified anymore, there is no leak of information. For staged sessions, further
             // validation is performed by the staging manager.
@@ -1161,6 +1281,8 @@
                         params.appPackageName, PackageManager.GET_SIGNATURES
                                 | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
 
+                prepareDataLoader();
+
                 if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
                     validateApexInstallLocked();
                 } else {
@@ -1173,6 +1295,8 @@
             }
         } catch (PackageManagerException e) {
             throw onSessionVerificationFailure(e);
+        } catch (StreamingException e) {
+            throw e;
         } catch (Throwable e) {
             // Convert all exceptions into package manager exceptions as only those are handled
             // in the code above.
@@ -1208,6 +1332,8 @@
         synchronized (mLock) {
             try {
                 sealAndValidateLocked(childSessions);
+            } catch (StreamingException e) {
+                Slog.e(TAG, "Streaming failed", e);
             } catch (PackageManagerException e) {
                 Slog.e(TAG, "Package not valid", e);
             }
@@ -1277,6 +1403,8 @@
 
                 try {
                     sealAndValidateLocked(childSessions);
+                } catch (StreamingException e) {
+                    throw new IllegalArgumentException("Streaming failed", e);
                 } catch (PackageManagerException e) {
                     throw new IllegalArgumentException("Package is not valid", e);
                 }
@@ -2224,6 +2352,132 @@
     }
 
     @Override
+    public void addFile(String name, long lengthBytes, byte[] metadata) {
+        if (mIncrementalFileStorages != null) {
+            try {
+                mIncrementalFileStorages.addFile(new InstallationFile(name, lengthBytes, metadata));
+            } catch (IOException ex) {
+                throw new IllegalStateException(
+                        "Failed to add and configure Incremental File: " + name, ex);
+            }
+        }
+        if (!isDataLoaderInstallation()) {
+            throw new IllegalStateException(
+                    "Cannot add files to non-callback installation session.");
+        }
+        // Use installer provided name for now; we always rename later
+        if (!FileUtils.isValidExtFilename(name)) {
+            throw new IllegalArgumentException("Invalid name: " + name);
+        }
+
+        synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+            assertPreparedAndNotSealedLocked("addFile");
+
+            mFiles.add(FileInfo.added(name, lengthBytes, metadata));
+        }
+    }
+
+    @Override
+    public void removeFile(String name) {
+        if (!isDataLoaderInstallation()) {
+            throw new IllegalStateException(
+                    "Cannot add files to non-callback installation session.");
+        }
+        if (TextUtils.isEmpty(params.appPackageName)) {
+            throw new IllegalStateException("Must specify package name to remove a split");
+        }
+
+        synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+            assertPreparedAndNotSealedLocked("removeFile");
+
+            mFiles.add(FileInfo.removed(getRemoveMarkerName(name)));
+        }
+    }
+
+    /**
+     * Makes sure files are present in staging location.
+     */
+    private void prepareDataLoader()
+            throws PackageManagerException, StreamingException {
+        if (!isDataLoaderInstallation()) {
+            return;
+        }
+
+        FilesystemConnector connector = new FilesystemConnector();
+
+        FileInfo[] addedFiles = mFiles.stream().filter(
+                file -> sAddedFilter.accept(new File(file.name))).toArray(FileInfo[]::new);
+        String[] removedFiles = mFiles.stream().filter(
+                file -> sRemovedFilter.accept(new File(file.name))).map(
+                    file -> file.name.substring(0,
+                        file.name.length() - REMOVE_MARKER_EXTENSION.length())).toArray(
+                String[]::new);
+
+        DataLoader dataLoader = new DataLoader();
+        try {
+            dataLoader.onCreate(connector);
+
+            if (!dataLoader.onPrepareImage(addedFiles, removedFiles)) {
+                throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+                        "Failed to prepare image.");
+            }
+        } catch (IOException e) {
+            throw new StreamingException(e);
+        } finally {
+            dataLoader.onDestroy();
+        }
+    }
+
+    // TODO(b/146080380): implement DataLoader using Incremental infrastructure.
+    class FilesystemConnector {
+        void writeData(FileInfo fileInfo, long offset, long lengthBytes,
+                ParcelFileDescriptor incomingFd) throws IOException {
+            doWriteInternal(fileInfo.name, offset, lengthBytes, incomingFd);
+        }
+    }
+
+    static class DataLoader {
+        private ParcelFileDescriptor mInFd = null;
+        private FilesystemConnector mConnector = null;
+
+        void onCreate(FilesystemConnector connector) throws IOException {
+            mConnector = connector;
+        }
+
+        void onDestroy() {
+            IoUtils.closeQuietly(mInFd);
+        }
+
+        private static final String STDIN_PATH = "-";
+        boolean onPrepareImage(FileInfo[] addedFiles, String[] removedFiles) throws IOException {
+            for (FileInfo fileInfo : addedFiles) {
+                String filePath = new String(fileInfo.metadata, StandardCharsets.UTF_8);
+                if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) {
+                    if (mInFd == null) {
+                        Slog.e(TAG, "Invalid stdin file descriptor.");
+                        return false;
+                    }
+                    ParcelFileDescriptor inFd = ParcelFileDescriptor.dup(mInFd.getFileDescriptor());
+                    mConnector.writeData(fileInfo, 0, fileInfo.lengthBytes, inFd);
+                } else {
+                    File localFile = new File(filePath);
+                    ParcelFileDescriptor incomingFd = null;
+                    try {
+                        incomingFd = ParcelFileDescriptor.open(localFile,
+                                ParcelFileDescriptor.MODE_READ_ONLY);
+                        mConnector.writeData(fileInfo, 0, localFile.length(), incomingFd);
+                    } finally {
+                        IoUtils.closeQuietly(incomingFd);
+                    }
+                }
+            }
+            return true;
+        }
+    }
+
+    @Override
     public int[] getChildSessionIds() {
         final int[] childSessionIds = mChildSessionIds.copyKeys();
         if (childSessionIds != null) {
@@ -2295,20 +2549,6 @@
         return mParentSessionId;
     }
 
-    @Override
-    public void addFile(@NonNull String name, long size, @NonNull byte[] metadata) {
-        if (mIncrementalFileStorages == null) {
-            throw new IllegalStateException(
-                    "Cannot add Incremental File to a non-Incremental session.");
-        }
-        try {
-            mIncrementalFileStorages.addFile(new InstallationFile(name, size, metadata));
-        } catch (IOException ex) {
-            throw new IllegalStateException(
-                    "Failed to add and configure Incremental File: " + name, ex);
-        }
-    }
-
     private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
         final IntentSender statusReceiver;
         final String packageName;
@@ -2321,7 +2561,7 @@
         }
 
         if (statusReceiver != null) {
-            // Execute observer.onPackageInstalled on different tread as we don't want callers
+            // Execute observer.onPackageInstalled on different thread as we don't want callers
             // inside the system server have to worry about catching the callbacks while they are
             // calling into the session
             final SomeArgs args = SomeArgs.obtain();
@@ -2595,6 +2835,8 @@
             writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
             writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
 
+            writeStringAttribute(out, ATTR_DATA_LOADER_PACKAGE_NAME, params.dataLoaderPackageName);
+
             writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
             writeWhitelistedRestrictedPermissionsLocked(out,
                     params.whitelistedRestrictedPermissions);
@@ -2624,6 +2866,13 @@
                 writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
                 out.endTag(null, TAG_CHILD_SESSION);
             }
+            for (FileInfo fileInfo : mFiles) {
+                out.startTag(null, TAG_SESSION_FILE);
+                writeStringAttribute(out, ATTR_NAME, fileInfo.name);
+                writeLongAttribute(out, ATTR_LENGTH_BYTES, fileInfo.lengthBytes);
+                writeByteArrayAttribute(out, ATTR_METADATA, fileInfo.metadata);
+                out.endTag(null, TAG_SESSION_FILE);
+            }
         }
 
         out.endTag(null, TAG_SESSION);
@@ -2697,6 +2946,8 @@
         params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
         params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
 
+        params.dataLoaderPackageName = readStringAttribute(in, ATTR_DATA_LOADER_PACKAGE_NAME);
+
         final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
         if (appIconFile.exists()) {
             params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
@@ -2723,6 +2974,7 @@
         List<String> grantedRuntimePermissions = new ArrayList<>();
         List<String> whitelistedRestrictedPermissions = new ArrayList<>();
         List<Integer> childSessionIds = new ArrayList<>();
+        List<FileInfo> files = new ArrayList<>();
         int outerDepth = in.getDepth();
         int type;
         while ((type = in.next()) != XmlPullParser.END_DOCUMENT
@@ -2740,6 +2992,11 @@
             if (TAG_CHILD_SESSION.equals(in.getName())) {
                 childSessionIds.add(readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID));
             }
+            if (TAG_SESSION_FILE.equals(in.getName())) {
+                files.add(new FileInfo(readStringAttribute(in, ATTR_NAME),
+                        readLongAttribute(in, ATTR_LENGTH_BYTES, -1),
+                        readByteArrayAttribute(in, ATTR_METADATA)));
+            }
         }
 
         if (grantedRuntimePermissions.size() > 0) {
@@ -2758,11 +3015,16 @@
             childSessionIdsArray = EMPTY_CHILD_SESSION_ARRAY;
         }
 
+        FileInfo[] fileInfosArray = null;
+        if (!files.isEmpty()) {
+            fileInfosArray = files.stream().toArray(FileInfo[]::new);
+        }
+
         InstallSource installSource = InstallSource.create(installInitiatingPackageName,
                 installOriginatingPackageName, installerPackageName, false);
         return new PackageInstallerSession(callback, context, pm, sessionProvider,
                 installerThread, stagingManager, sessionId, userId, installerUid,
-                installSource, params, createdMillis, stageDir, stageCid,
+                installSource, params, createdMillis, stageDir, stageCid, fileInfosArray,
                 prepared, committed, sealed, childSessionIdsArray, parentSessionId,
                 isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ea9a0cb..1153fb5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -155,6 +155,7 @@
 import android.content.pm.InstallSourceInfo;
 import android.content.pm.InstantAppInfo;
 import android.content.pm.InstantAppRequest;
+import android.content.pm.InstantAppResolveInfo.InstantAppDigest;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
@@ -376,6 +377,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -6143,10 +6145,12 @@
 
     private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
             Intent origIntent, String resolvedType, String callingPackage,
-            Bundle verificationBundle, int userId) {
+            boolean isRequesterInstantApp, Bundle verificationBundle, int userId) {
         final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO,
                 new InstantAppRequest(responseObj, origIntent, resolvedType,
-                        callingPackage, userId, verificationBundle, false /*resolveForStart*/));
+                        callingPackage, isRequesterInstantApp, userId, verificationBundle,
+                        false /*resolveForStart*/, responseObj.hostDigestPrefixSecure,
+                        responseObj.token));
         mHandler.sendMessage(msg);
     }
 
@@ -6765,8 +6769,10 @@
             }
         }
         if (addInstant) {
-            result = maybeAddInstantAppInstaller(
-                    result, intent, resolvedType, flags, userId, resolveForStart);
+            String callingPkgName = getInstantAppPackageName(filterCallingUid);
+            boolean isRequesterInstantApp = isInstantApp(callingPkgName, userId);
+            result = maybeAddInstantAppInstaller(result, intent, resolvedType, flags, userId,
+                    resolveForStart, isRequesterInstantApp);
         }
         if (sortResult) {
             Collections.sort(result, RESOLVE_PRIORITY_SORTER);
@@ -6777,7 +6783,8 @@
     }
 
     private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result, Intent intent,
-            String resolvedType, int flags, int userId, boolean resolveForStart) {
+            String resolvedType, int flags, int userId, boolean resolveForStart,
+            boolean isRequesterInstantApp) {
         // first, check to see if we've got an instant app already installed
         final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0;
         ResolveInfo localInstantApp = null;
@@ -6825,10 +6832,13 @@
             if (localInstantApp == null) {
                 // we don't have an instant app locally, resolve externally
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
+                String token = UUID.randomUUID().toString();
+                InstantAppDigest digest = InstantAppResolver.parseDigest(intent);
                 final InstantAppRequest requestObject = new InstantAppRequest(
                         null /*responseObj*/, intent /*origIntent*/, resolvedType,
-                        null /*callingPackage*/, userId, null /*verificationBundle*/,
-                        resolveForStart);
+                        null /*callingPackage*/, isRequesterInstantApp,
+                        userId, null /*verificationBundle*/, resolveForStart,
+                        digest.getDigestPrefixSecure(), token);
                 auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(
                         mInstantAppResolverConnection, requestObject);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -22748,6 +22758,12 @@
         }
 
         @Override
+        public ComponentName getSystemUiServiceComponent() {
+            return ComponentName.unflattenFromString(mContext.getResources().getString(
+                    com.android.internal.R.string.config_systemUIServiceComponent));
+        }
+
+        @Override
         public void setDeviceAndProfileOwnerPackages(
                 int deviceOwnerUserId, String deviceOwnerPackage,
                 SparseArray<String> profileOwnerPackages) {
@@ -22827,10 +22843,10 @@
         @Override
         public void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
                 Intent origIntent, String resolvedType, String callingPackage,
-                Bundle verificationBundle, int userId) {
+                boolean isRequesterInstantApp, Bundle verificationBundle, int userId) {
             PackageManagerService.this.requestInstantAppResolutionPhaseTwo(
-                    responseObj, origIntent, resolvedType, callingPackage, verificationBundle,
-                    userId);
+                    responseObj, origIntent, resolvedType, callingPackage, isRequesterInstantApp,
+                    verificationBundle, userId);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index fff404f..dfffbd6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -114,6 +114,7 @@
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -135,6 +136,8 @@
     private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/";
     private static final int DEFAULT_WAIT_MS = 60 * 1000;
 
+    private static final String PM_SHELL_DATALOADER = "com.android.pm.dataloader";
+
     final IPackageManager mInterface;
     final IPermissionManager mPermissionManager;
     final private WeakHashMap<String, Resources> mResourceCache =
@@ -175,6 +178,8 @@
                     return runQueryIntentReceivers();
                 case "install":
                     return runInstall();
+                case "install-streaming":
+                    return runStreamingInstall();
                 case "install-abandon":
                 case "install-destroy":
                     return runInstallAbandon();
@@ -1152,9 +1157,21 @@
         return 0;
     }
 
-    private int runInstall() throws RemoteException {
-        final PrintWriter pw = getOutPrintWriter();
+    private int runStreamingInstall() throws RemoteException {
         final InstallParams params = makeInstallParams();
+        if (TextUtils.isEmpty(params.sessionParams.dataLoaderPackageName)) {
+            params.sessionParams.setDataLoaderPackageName(PM_SHELL_DATALOADER);
+        }
+        return doRunInstall(params);
+    }
+
+    private int runInstall() throws RemoteException {
+        return doRunInstall(makeInstallParams());
+    }
+
+    private int doRunInstall(final InstallParams params) throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final boolean streaming = !TextUtils.isEmpty(params.sessionParams.dataLoaderPackageName);
 
         ArrayList<String> inPaths = getRemainingArgs();
         if (inPaths.isEmpty()) {
@@ -1181,17 +1198,30 @@
             return 1;
         }
 
-        setParamsSize(params, inPaths);
+        if (!streaming) {
+            setParamsSize(params, inPaths);
+        }
+
         final int sessionId = doCreateSession(params.sessionParams,
                 params.installerPackageName, params.userId);
         boolean abandonSession = true;
         try {
             for (String inPath : inPaths) {
-                String splitName = hasSplits ? (new File(inPath)).getName()
-                        : "base." + (isApex ? "apex" : "apk");
-                if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, splitName,
-                        false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
-                    return 1;
+                if (streaming) {
+                    String name = new File(inPath).getName();
+                    byte[] metadata = inPath.getBytes(StandardCharsets.UTF_8);
+                    if (doAddFile(sessionId, name, params.sessionParams.sizeBytes, metadata,
+                            false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
+                        return 1;
+                    }
+                } else {
+                    String splitName = hasSplits ? new File(inPath).getName()
+                            : "base." + (isApex ? "apex" : "apk");
+
+                    if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, splitName,
+                            false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
+                        return 1;
+                    }
                 }
             }
             if (doCommitSession(sessionId, false /*logSuccess*/)
@@ -2927,11 +2957,32 @@
         return sessionId;
     }
 
+    private int doAddFile(int sessionId, String name, long sizeBytes, byte[] metadata,
+            boolean logSuccess) throws RemoteException {
+        PackageInstaller.Session session = new PackageInstaller.Session(
+                mInterface.getPackageInstaller().openSession(sessionId));
+        try {
+            session.addFile(name, sizeBytes, metadata);
+
+            if (logSuccess) {
+                getOutPrintWriter().println("Success");
+            }
+
+            return 0;
+        } finally {
+            IoUtils.closeQuietly(session);
+        }
+    }
+
     private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
             boolean logSuccess) throws RemoteException {
         PackageInstaller.Session session = null;
         try {
+            session = new PackageInstaller.Session(
+                    mInterface.getPackageInstaller().openSession(sessionId));
+
             final PrintWriter pw = getOutPrintWriter();
+
             final ParcelFileDescriptor fd;
             if (STDIN_PATH.equals(inPath)) {
                 fd = ParcelFileDescriptor.dup(getInFileDescriptor());
@@ -2953,8 +3004,6 @@
                 return 1;
             }
 
-            session = new PackageInstaller.Session(
-                    mInterface.getPackageInstaller().openSession(sessionId));
             session.write(splitName, 0, sizeBytes, fd);
 
             if (logSuccess) {
@@ -3000,7 +3049,6 @@
         try {
             session = new PackageInstaller.Session(
                     mInterface.getPackageInstaller().openSession(sessionId));
-
             for (String splitName : splitNames) {
                 session.removeSplit(splitName);
             }
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index e8798ff..59a5804 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -24,7 +24,20 @@
       ]
     },
     {
-      "name": "PackageManagerShellCommandTest"
+      "name": "CtsContentTestCases",
+      "options": [
+        {
+          "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest"
+        }
+      ]
+    },
+    {
+      "name": "GtsSecurityHostTestCases",
+      "options": [
+        {
+          "include-filter": "com.google.android.security.gts.PackageVerifierTest"
+        }
+      ]
     }
   ],
   "postsubmit": [
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4d436c0..5fabdb6 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -351,6 +351,7 @@
      * User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
      * that should be applied to all users, including guests. Only non-empty restriction bundles are
      * stored.
+     * The key is the user id of the user whom the restriction originated from.
      */
     @GuardedBy("mRestrictionsLock")
     private final SparseArray<Bundle> mDevicePolicyGlobalUserRestrictions = new SparseArray<>();
@@ -364,6 +365,7 @@
     /**
      * User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
      * for each user. Only non-empty restriction bundles are stored.
+     * The key is the user id of the user whom the restriction originated from.
      */
     @GuardedBy("mRestrictionsLock")
     private final SparseArray<Bundle> mDevicePolicyLocalUserRestrictions = new SparseArray<>();
@@ -1621,7 +1623,7 @@
     /**
      * See {@link UserManagerInternal#setDevicePolicyUserRestrictions}
      */
-    private void setDevicePolicyUserRestrictionsInner(@UserIdInt int userId,
+    private void setDevicePolicyUserRestrictionsInner(@UserIdInt int originatingUserId,
             @Nullable Bundle restrictions,
             @UserManagerInternal.OwnerType int restrictionOwnerType) {
         final Bundle global = new Bundle();
@@ -1635,16 +1637,16 @@
         synchronized (mRestrictionsLock) {
             // Update global and local restrictions if they were changed.
             globalChanged = updateRestrictionsIfNeededLR(
-                    userId, global, mDevicePolicyGlobalUserRestrictions);
+                    originatingUserId, global, mDevicePolicyGlobalUserRestrictions);
             localChanged = updateRestrictionsIfNeededLR(
-                    userId, local, mDevicePolicyLocalUserRestrictions);
+                    originatingUserId, local, mDevicePolicyLocalUserRestrictions);
 
             if (restrictionOwnerType == UserManagerInternal.OWNER_TYPE_DEVICE_OWNER) {
                 // Remember the global restriction owner userId to be able to make a distinction
                 // in getUserRestrictionSource on who set local policies.
-                mDeviceOwnerUserId = userId;
+                mDeviceOwnerUserId = originatingUserId;
             } else {
-                if (mDeviceOwnerUserId == userId) {
+                if (mDeviceOwnerUserId == originatingUserId) {
                     // When profile owner sets restrictions it passes null global bundle and we
                     // reset global restriction owner userId.
                     // This means this user used to have DO, but now the DO is gone and the user
@@ -1654,15 +1656,16 @@
             }
         }
         if (DBG) {
-            Log.d(LOG_TAG, "setDevicePolicyUserRestrictions: userId=" + userId
-                            + " global=" + global + (globalChanged ? " (changed)" : "")
-                            + " local=" + local + (localChanged ? " (changed)" : "")
+            Log.d(LOG_TAG, "setDevicePolicyUserRestrictions: "
+                    + " originatingUserId=" + originatingUserId
+                    + " global=" + global + (globalChanged ? " (changed)" : "")
+                    + " local=" + local + (localChanged ? " (changed)" : "")
             );
         }
         // Don't call them within the mRestrictionsLock.
         synchronized (mPackagesLock) {
             if (localChanged || globalChanged) {
-                writeUserLP(getUserDataNoChecks(userId));
+                writeUserLP(getUserDataNoChecks(originatingUserId));
             }
         }
 
@@ -1670,7 +1673,7 @@
             if (globalChanged) {
                 applyUserRestrictionsForAllUsersLR();
             } else if (localChanged) {
-                applyUserRestrictionsLR(userId);
+                applyUserRestrictionsLR(originatingUserId);
             }
         }
     }
@@ -4507,9 +4510,10 @@
 
     private class LocalService extends UserManagerInternal {
         @Override
-        public void setDevicePolicyUserRestrictions(@UserIdInt int userId,
-                @Nullable Bundle restrictions, @OwnerType int restrictionOwnerType) {
-            UserManagerService.this.setDevicePolicyUserRestrictionsInner(userId,
+        public void setDevicePolicyUserRestrictions(@UserIdInt int originatingUserId,
+                @Nullable Bundle restrictions,
+                @OwnerType int restrictionOwnerType) {
+            UserManagerService.this.setDevicePolicyUserRestrictionsInner(originatingUserId,
                     restrictions, restrictionOwnerType);
         }
 
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 0beff7a..e0bd0b4 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -411,6 +411,13 @@
     }
 
     /**
+     * @return true if a restriction is settable by profile owner of an organization owned device.
+     */
+    public static boolean canProfileOwnerOfOrganizationOwnedDeviceChange(String restriction) {
+        return PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS.contains(restriction);
+    }
+
+    /**
      * Returns the user restrictions that default to {@code true} for device owners.
      * These user restrictions are local, though. ie only for the device owner's user id.
      */
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/AudioSessionProviderImpl.java b/services/core/java/com/android/server/soundtrigger_middleware/AudioSessionProviderImpl.java
new file mode 100644
index 0000000..3fa5230
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/AudioSessionProviderImpl.java
@@ -0,0 +1,29 @@
+/*
+ * 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.soundtrigger_middleware;
+
+/**
+ * An implementation of SoundTriggerMiddlewareImpl.AudioSessionProvider that ties to native
+ * AudioSystem module via JNI.
+ */
+class AudioSessionProviderImpl extends SoundTriggerMiddlewareImpl.AudioSessionProvider {
+    @Override
+    public native AudioSession acquireSession();
+
+    @Override
+    public native void releaseSession(int sessionHandle);
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
new file mode 100644
index 0000000..9b22f33
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -0,0 +1,390 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.audio.common.V2_0.Uuid;
+import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
+import android.hardware.soundtrigger.V2_3.ISoundTriggerHw;
+import android.media.audio.common.AudioConfig;
+import android.media.audio.common.AudioOffloadInfo;
+import android.media.soundtrigger_middleware.ConfidenceLevel;
+import android.media.soundtrigger_middleware.ModelParameter;
+import android.media.soundtrigger_middleware.ModelParameterRange;
+import android.media.soundtrigger_middleware.Phrase;
+import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
+import android.media.soundtrigger_middleware.PhraseSoundModel;
+import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger_middleware.RecognitionMode;
+import android.media.soundtrigger_middleware.RecognitionStatus;
+import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger_middleware.SoundModelType;
+import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
+import android.os.HidlMemoryUtil;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Utilities for type conversion between SoundTrigger HAL types and SoundTriggerMiddleware service
+ * types.
+ *
+ * @hide
+ */
+class ConversionUtil {
+    static @NonNull
+    SoundTriggerModuleProperties hidl2aidlProperties(
+            @NonNull ISoundTriggerHw.Properties hidlProperties) {
+        SoundTriggerModuleProperties aidlProperties = new SoundTriggerModuleProperties();
+        aidlProperties.implementor = hidlProperties.implementor;
+        aidlProperties.description = hidlProperties.description;
+        aidlProperties.version = hidlProperties.version;
+        aidlProperties.uuid = hidl2aidlUuid(hidlProperties.uuid);
+        aidlProperties.maxSoundModels = hidlProperties.maxSoundModels;
+        aidlProperties.maxKeyPhrases = hidlProperties.maxKeyPhrases;
+        aidlProperties.maxUsers = hidlProperties.maxUsers;
+        aidlProperties.recognitionModes = hidlProperties.recognitionModes;
+        aidlProperties.captureTransition = hidlProperties.captureTransition;
+        aidlProperties.maxBufferMs = hidlProperties.maxBufferMs;
+        aidlProperties.concurrentCapture = hidlProperties.concurrentCapture;
+        aidlProperties.triggerInEvent = hidlProperties.triggerInEvent;
+        aidlProperties.powerConsumptionMw = hidlProperties.powerConsumptionMw;
+        return aidlProperties;
+    }
+
+    static @NonNull
+    String hidl2aidlUuid(@NonNull Uuid hidlUuid) {
+        if (hidlUuid.node == null || hidlUuid.node.length != 6) {
+            throw new IllegalArgumentException("UUID.node must be of length 6.");
+        }
+        return String.format(UuidUtil.FORMAT,
+                hidlUuid.timeLow,
+                hidlUuid.timeMid,
+                hidlUuid.versionAndTimeHigh,
+                hidlUuid.variantAndClockSeqHigh,
+                hidlUuid.node[0],
+                hidlUuid.node[1],
+                hidlUuid.node[2],
+                hidlUuid.node[3],
+                hidlUuid.node[4],
+                hidlUuid.node[5]);
+    }
+
+    static @NonNull
+    Uuid aidl2hidlUuid(@NonNull String aidlUuid) {
+        Matcher matcher = UuidUtil.PATTERN.matcher(aidlUuid);
+        if (!matcher.matches()) {
+            throw new IllegalArgumentException("Illegal format for UUID: " + aidlUuid);
+        }
+        Uuid hidlUuid = new Uuid();
+        hidlUuid.timeLow = Integer.parseUnsignedInt(matcher.group(1), 16);
+        hidlUuid.timeMid = (short) Integer.parseUnsignedInt(matcher.group(2), 16);
+        hidlUuid.versionAndTimeHigh = (short) Integer.parseUnsignedInt(matcher.group(3), 16);
+        hidlUuid.variantAndClockSeqHigh = (short) Integer.parseUnsignedInt(matcher.group(4), 16);
+        hidlUuid.node = new byte[]{(byte) Integer.parseUnsignedInt(matcher.group(5), 16),
+                (byte) Integer.parseUnsignedInt(matcher.group(6), 16),
+                (byte) Integer.parseUnsignedInt(matcher.group(7), 16),
+                (byte) Integer.parseUnsignedInt(matcher.group(8), 16),
+                (byte) Integer.parseUnsignedInt(matcher.group(9), 16),
+                (byte) Integer.parseUnsignedInt(matcher.group(10), 16)};
+        return hidlUuid;
+    }
+
+    static int aidl2hidlSoundModelType(int aidlType) {
+        switch (aidlType) {
+            case SoundModelType.GENERIC:
+                return android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC;
+            case SoundModelType.KEYPHRASE:
+                return android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE;
+            default:
+                throw new IllegalArgumentException("Unknown sound model type: " + aidlType);
+        }
+    }
+
+    static int hidl2aidlSoundModelType(int hidlType) {
+        switch (hidlType) {
+            case android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC:
+                return SoundModelType.GENERIC;
+            case android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE:
+                return SoundModelType.KEYPHRASE;
+            default:
+                throw new IllegalArgumentException("Unknown sound model type: " + hidlType);
+        }
+    }
+
+    static @NonNull
+    ISoundTriggerHw.Phrase aidl2hidlPhrase(@NonNull Phrase aidlPhrase) {
+        ISoundTriggerHw.Phrase hidlPhrase = new ISoundTriggerHw.Phrase();
+        hidlPhrase.id = aidlPhrase.id;
+        hidlPhrase.recognitionModes = aidl2hidlRecognitionModes(aidlPhrase.recognitionModes);
+        for (int aidlUser : aidlPhrase.users) {
+            hidlPhrase.users.add(aidlUser);
+        }
+        hidlPhrase.locale = aidlPhrase.locale;
+        hidlPhrase.text = aidlPhrase.text;
+        return hidlPhrase;
+    }
+
+    static int aidl2hidlRecognitionModes(int aidlModes) {
+        int hidlModes = 0;
+
+        if ((aidlModes & RecognitionMode.VOICE_TRIGGER) != 0) {
+            hidlModes |= android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER;
+        }
+        if ((aidlModes & RecognitionMode.USER_IDENTIFICATION) != 0) {
+            hidlModes |= android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION;
+        }
+        if ((aidlModes & RecognitionMode.USER_AUTHENTICATION) != 0) {
+            hidlModes |= android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION;
+        }
+        if ((aidlModes & RecognitionMode.GENERIC_TRIGGER) != 0) {
+            hidlModes |= android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+        }
+        return hidlModes;
+    }
+
+    static int hidl2aidlRecognitionModes(int hidlModes) {
+        int aidlModes = 0;
+        if ((hidlModes & android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER) != 0) {
+            aidlModes |= RecognitionMode.VOICE_TRIGGER;
+        }
+        if ((hidlModes & android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION)
+                != 0) {
+            aidlModes |= RecognitionMode.USER_IDENTIFICATION;
+        }
+        if ((hidlModes & android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION)
+                != 0) {
+            aidlModes |= RecognitionMode.USER_AUTHENTICATION;
+        }
+        if ((hidlModes & android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER) != 0) {
+            aidlModes |= RecognitionMode.GENERIC_TRIGGER;
+        }
+        return aidlModes;
+    }
+
+    static @NonNull
+    ISoundTriggerHw.SoundModel aidl2hidlSoundModel(@NonNull SoundModel aidlModel) {
+        ISoundTriggerHw.SoundModel hidlModel = new ISoundTriggerHw.SoundModel();
+        hidlModel.header.type = aidl2hidlSoundModelType(aidlModel.type);
+        hidlModel.header.uuid = aidl2hidlUuid(aidlModel.uuid);
+        hidlModel.header.vendorUuid = aidl2hidlUuid(aidlModel.vendorUuid);
+        hidlModel.data = HidlMemoryUtil.byteArrayToHidlMemory(aidlModel.data,
+                "SoundTrigger SoundModel");
+        return hidlModel;
+    }
+
+    static @NonNull
+    ISoundTriggerHw.PhraseSoundModel aidl2hidlPhraseSoundModel(
+            @NonNull PhraseSoundModel aidlModel) {
+        ISoundTriggerHw.PhraseSoundModel hidlModel = new ISoundTriggerHw.PhraseSoundModel();
+        hidlModel.common = aidl2hidlSoundModel(aidlModel.common);
+        for (Phrase aidlPhrase : aidlModel.phrases) {
+            hidlModel.phrases.add(aidl2hidlPhrase(aidlPhrase));
+        }
+        return hidlModel;
+    }
+
+    static @NonNull
+    ISoundTriggerHw.RecognitionConfig aidl2hidlRecognitionConfig(
+            @NonNull RecognitionConfig aidlConfig) {
+        ISoundTriggerHw.RecognitionConfig hidlConfig = new ISoundTriggerHw.RecognitionConfig();
+        hidlConfig.header.captureRequested = aidlConfig.captureRequested;
+        for (PhraseRecognitionExtra aidlPhraseExtra : aidlConfig.phraseRecognitionExtras) {
+            hidlConfig.header.phrases.add(aidl2hidlPhraseRecognitionExtra(aidlPhraseExtra));
+        }
+        hidlConfig.data = HidlMemoryUtil.byteArrayToHidlMemory(aidlConfig.data,
+                "SoundTrigger RecognitionConfig");
+        return hidlConfig;
+    }
+
+    static @NonNull
+    android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra aidl2hidlPhraseRecognitionExtra(
+            @NonNull PhraseRecognitionExtra aidlExtra) {
+        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra hidlExtra =
+                new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
+        hidlExtra.id = aidlExtra.id;
+        hidlExtra.recognitionModes = aidl2hidlRecognitionModes(aidlExtra.recognitionModes);
+        hidlExtra.confidenceLevel = aidlExtra.confidenceLevel;
+        hidlExtra.levels.ensureCapacity(aidlExtra.levels.length);
+        for (ConfidenceLevel aidlLevel : aidlExtra.levels) {
+            hidlExtra.levels.add(aidl2hidlConfidenceLevel(aidlLevel));
+        }
+        return hidlExtra;
+    }
+
+    static @NonNull
+    PhraseRecognitionExtra hidl2aidlPhraseRecognitionExtra(
+            @NonNull android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra hidlExtra) {
+        PhraseRecognitionExtra aidlExtra = new PhraseRecognitionExtra();
+        aidlExtra.id = hidlExtra.id;
+        aidlExtra.recognitionModes = hidl2aidlRecognitionModes(hidlExtra.recognitionModes);
+        aidlExtra.confidenceLevel = hidlExtra.confidenceLevel;
+        aidlExtra.levels = new ConfidenceLevel[hidlExtra.levels.size()];
+        for (int i = 0; i < hidlExtra.levels.size(); ++i) {
+            aidlExtra.levels[i] = hidl2aidlConfidenceLevel(hidlExtra.levels.get(i));
+        }
+        return aidlExtra;
+    }
+
+    static @NonNull
+    android.hardware.soundtrigger.V2_0.ConfidenceLevel aidl2hidlConfidenceLevel(
+            @NonNull ConfidenceLevel aidlLevel) {
+        android.hardware.soundtrigger.V2_0.ConfidenceLevel hidlLevel =
+                new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
+        hidlLevel.userId = aidlLevel.userId;
+        hidlLevel.levelPercent = aidlLevel.levelPercent;
+        return hidlLevel;
+    }
+
+    static @NonNull
+    ConfidenceLevel hidl2aidlConfidenceLevel(
+            @NonNull android.hardware.soundtrigger.V2_0.ConfidenceLevel hidlLevel) {
+        ConfidenceLevel aidlLevel = new ConfidenceLevel();
+        aidlLevel.userId = hidlLevel.userId;
+        aidlLevel.levelPercent = hidlLevel.levelPercent;
+        return aidlLevel;
+    }
+
+    static int hidl2aidlRecognitionStatus(int hidlStatus) {
+        switch (hidlStatus) {
+            case ISoundTriggerHwCallback.RecognitionStatus.SUCCESS:
+                return RecognitionStatus.SUCCESS;
+            case ISoundTriggerHwCallback.RecognitionStatus.ABORT:
+                return RecognitionStatus.ABORTED;
+            case ISoundTriggerHwCallback.RecognitionStatus.FAILURE:
+                return RecognitionStatus.FAILURE;
+            case 3: // This doesn't have a constant in HIDL.
+                return RecognitionStatus.FORCED;
+            default:
+                throw new IllegalArgumentException("Unknown recognition status: " + hidlStatus);
+        }
+    }
+
+    static @NonNull
+    RecognitionEvent hidl2aidlRecognitionEvent(@NonNull
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent hidlEvent) {
+        RecognitionEvent aidlEvent = new RecognitionEvent();
+        aidlEvent.status = hidl2aidlRecognitionStatus(hidlEvent.status);
+        aidlEvent.type = hidl2aidlSoundModelType(hidlEvent.type);
+        aidlEvent.captureAvailable = hidlEvent.captureAvailable;
+        // hidlEvent.captureSession is never a valid field.
+        aidlEvent.captureSession = -1;
+        aidlEvent.captureDelayMs = hidlEvent.captureDelayMs;
+        aidlEvent.capturePreambleMs = hidlEvent.capturePreambleMs;
+        aidlEvent.triggerInData = hidlEvent.triggerInData;
+        aidlEvent.audioConfig = hidl2aidlAudioConfig(hidlEvent.audioConfig);
+        aidlEvent.data = new byte[hidlEvent.data.size()];
+        for (int i = 0; i < aidlEvent.data.length; ++i) {
+            aidlEvent.data[i] = hidlEvent.data.get(i);
+        }
+        return aidlEvent;
+    }
+
+    static @NonNull
+    RecognitionEvent hidl2aidlRecognitionEvent(
+            @NonNull ISoundTriggerHwCallback.RecognitionEvent hidlEvent) {
+        RecognitionEvent aidlEvent = hidl2aidlRecognitionEvent(hidlEvent.header);
+        // Data needs to get overridden with 2.1 data.
+        aidlEvent.data = HidlMemoryUtil.hidlMemoryToByteArray(hidlEvent.data);
+        return aidlEvent;
+    }
+
+    static @NonNull
+    PhraseRecognitionEvent hidl2aidlPhraseRecognitionEvent(@NonNull
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent hidlEvent) {
+        PhraseRecognitionEvent aidlEvent = new PhraseRecognitionEvent();
+        aidlEvent.common = hidl2aidlRecognitionEvent(hidlEvent.common);
+        aidlEvent.phraseExtras = new PhraseRecognitionExtra[hidlEvent.phraseExtras.size()];
+        for (int i = 0; i < hidlEvent.phraseExtras.size(); ++i) {
+            aidlEvent.phraseExtras[i] = hidl2aidlPhraseRecognitionExtra(
+                    hidlEvent.phraseExtras.get(i));
+        }
+        return aidlEvent;
+    }
+
+    static @NonNull
+    PhraseRecognitionEvent hidl2aidlPhraseRecognitionEvent(
+            @NonNull ISoundTriggerHwCallback.PhraseRecognitionEvent hidlEvent) {
+        PhraseRecognitionEvent aidlEvent = new PhraseRecognitionEvent();
+        aidlEvent.common = hidl2aidlRecognitionEvent(hidlEvent.common);
+        aidlEvent.phraseExtras = new PhraseRecognitionExtra[hidlEvent.phraseExtras.size()];
+        for (int i = 0; i < hidlEvent.phraseExtras.size(); ++i) {
+            aidlEvent.phraseExtras[i] = hidl2aidlPhraseRecognitionExtra(
+                    hidlEvent.phraseExtras.get(i));
+        }
+        return aidlEvent;
+    }
+
+    static @NonNull
+    AudioConfig hidl2aidlAudioConfig(
+            @NonNull android.hardware.audio.common.V2_0.AudioConfig hidlConfig) {
+        AudioConfig aidlConfig = new AudioConfig();
+        // TODO(ytai): channelMask and format might need a more careful conversion to make sure the
+        //  constants match.
+        aidlConfig.sampleRateHz = hidlConfig.sampleRateHz;
+        aidlConfig.channelMask = hidlConfig.channelMask;
+        aidlConfig.format = hidlConfig.format;
+        aidlConfig.offloadInfo = hidl2aidlOffloadInfo(hidlConfig.offloadInfo);
+        aidlConfig.frameCount = hidlConfig.frameCount;
+        return aidlConfig;
+    }
+
+    static @NonNull
+    AudioOffloadInfo hidl2aidlOffloadInfo(
+            @NonNull android.hardware.audio.common.V2_0.AudioOffloadInfo hidlInfo) {
+        AudioOffloadInfo aidlInfo = new AudioOffloadInfo();
+        // TODO(ytai): channelMask, format, streamType and usage might need a more careful
+        //  conversion to make sure the constants match.
+        aidlInfo.sampleRateHz = hidlInfo.sampleRateHz;
+        aidlInfo.channelMask = hidlInfo.channelMask;
+        aidlInfo.format = hidlInfo.format;
+        aidlInfo.streamType = hidlInfo.streamType;
+        aidlInfo.bitRatePerSecond = hidlInfo.bitRatePerSecond;
+        aidlInfo.durationMicroseconds = hidlInfo.durationMicroseconds;
+        aidlInfo.hasVideo = hidlInfo.hasVideo;
+        aidlInfo.isStreaming = hidlInfo.isStreaming;
+        aidlInfo.bitWidth = hidlInfo.bitWidth;
+        aidlInfo.bufferSize = hidlInfo.bufferSize;
+        aidlInfo.usage = hidlInfo.usage;
+        return aidlInfo;
+    }
+
+    @Nullable
+    static ModelParameterRange hidl2aidlModelParameterRange(
+            android.hardware.soundtrigger.V2_3.ModelParameterRange hidlRange) {
+        if (hidlRange == null) {
+            return null;
+        }
+        ModelParameterRange aidlRange = new ModelParameterRange();
+        aidlRange.minInclusive = hidlRange.start;
+        aidlRange.maxInclusive = hidlRange.end;
+        return aidlRange;
+    }
+
+    static int aidl2hidlModelParameter(int aidlParam) {
+        switch (aidlParam) {
+            case ModelParameter.THRESHOLD_FACTOR:
+                return android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR;
+
+            default:
+                return android.hardware.soundtrigger.V2_3.ModelParameter.INVALID;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/HalException.java b/services/core/java/com/android/server/soundtrigger_middleware/HalException.java
new file mode 100644
index 0000000..8b3e708
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/HalException.java
@@ -0,0 +1,49 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+
+/**
+ * This exception represents a non-zero status code returned by a HAL invocation.
+ * Depending on the operation that threw the error, the integrity of the HAL implementation and the
+ * client's tolerance to error, this error may or may not be recoverable. The HAL itself is expected
+ * to retain the state it had prior to the invocation (so, unless the error is a result of a HAL
+ * bug, normal operation may resume).
+ * <p>
+ * The reason why this is a RuntimeException, even though the HAL interface allows returning them
+ * is because we expect none of them to actually occur as part of correct usage of the HAL.
+ *
+ * @hide
+ */
+public class HalException extends RuntimeException {
+    public final int errorCode;
+
+    public HalException(int errorCode, @NonNull String message) {
+        super(message);
+        this.errorCode = errorCode;
+    }
+
+    public HalException(int errorCode) {
+        this.errorCode = errorCode;
+    }
+
+    @Override
+    public @NonNull String toString() {
+        return super.toString() + " (code " + errorCode + ")";
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
new file mode 100644
index 0000000..f0a0d83
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/Hw2CompatUtil.java
@@ -0,0 +1,77 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.os.HidlMemoryUtil;
+
+import java.util.ArrayList;
+
+/**
+ * Utilities for maintaining data compatibility between different minor versions of soundtrigger@2.x
+ * HAL.
+ * Note that some of these conversion utilities are destructive, i.e. mutate their input (for the
+ * sake of simplifying code and reducing copies).
+ */
+class Hw2CompatUtil {
+    static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel convertSoundModel_2_1_to_2_0(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel) {
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model_2_0 = soundModel.header;
+        // Note: this mutates the input!
+        model_2_0.data = HidlMemoryUtil.hidlMemoryToByteList(soundModel.data);
+        return model_2_0;
+    }
+
+    static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent convertRecognitionEvent_2_0_to_2_1(
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent event) {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event_2_1 =
+                new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent();
+        event_2_1.header = event;
+        event_2_1.data = HidlMemoryUtil.byteListToHidlMemory(event_2_1.header.data,
+                "SoundTrigger RecognitionEvent");
+        // Note: this mutates the input!
+        event_2_1.header.data = new ArrayList<>();
+        return event_2_1;
+    }
+
+    static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent convertPhraseRecognitionEvent_2_0_to_2_1(
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent event) {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent
+                event_2_1 =
+                new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent();
+        event_2_1.common = convertRecognitionEvent_2_0_to_2_1(event.common);
+        event_2_1.phraseExtras = event.phraseExtras;
+        return event_2_1;
+    }
+
+    static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel convertPhraseSoundModel_2_1_to_2_0(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel) {
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model_2_0 =
+                new android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel();
+        model_2_0.common = convertSoundModel_2_1_to_2_0(soundModel.common);
+        model_2_0.phrases = soundModel.phrases;
+        return model_2_0;
+    }
+
+    static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig convertRecognitionConfig_2_1_to_2_0(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config) {
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config_2_0 =
+                config.header;
+        // Note: this mutates the input!
+        config_2_0.data = HidlMemoryUtil.hidlMemoryToByteList(config.data);
+        return config_2_0;
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
new file mode 100644
index 0000000..81252c9
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
@@ -0,0 +1,158 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.hardware.soundtrigger.V2_3.ISoundTriggerHw;
+import android.hardware.soundtrigger.V2_3.ModelParameterRange;
+import android.hidl.base.V1_0.IBase;
+import android.os.IHwBinder;
+
+/**
+ * This interface mimics android.hardware.soundtrigger.V2_x.ISoundTriggerHw and
+ * android.hardware.soundtrigger.V2_x.ISoundTriggerHwCallback, with a few key differences:
+ * <ul>
+ * <li>Methods in the original interface generally have a status return value and potentially a
+ * second return value which is the actual return value. This is reflected via a synchronous
+ * callback, which is not very pleasant to work with. This interface replaces that pattern with
+ * the convention that a HalException is thrown for non-OK status, and then we can use the
+ * return value for the actual return value.
+ * <li>This interface will always include all the methods from the latest 2.x version (and thus
+ * from every 2.x version) interface, with the convention that unsupported methods throw a
+ * {@link RecoverableException} with a
+ * {@link android.media.soundtrigger_middleware.Status#OPERATION_NOT_SUPPORTED}
+ * code.
+ * <li>Cases where the original interface had multiple versions of a method representing the exact
+ * thing, or there exists a trivial conversion between the new and old version, this interface
+ * represents only the latest version, without any _version suffixes.
+ * <li>Removes some of the obscure IBinder methods.
+ * <li>No RemoteExceptions are specified. Some implementations of this interface may rethrow
+ * RemoteExceptions as RuntimeExceptions, some can guarantee handling them somehow and never throw
+ * them.
+ * <li>soundModelCallback has been removed, since nobody cares about it. Implementations are free
+ * to silently discard it.
+ * </ul>
+ * For cases where the client wants to explicitly handle specific versions of the underlying driver
+ * interface, they may call {@link #interfaceDescriptor()}.
+ * <p>
+ * <b>Note to maintainers</b>: This class must always be kept in sync with the latest 2.x version,
+ * so that clients have access to the entire functionality without having to burden themselves with
+ * compatibility, as much as possible.
+ */
+public interface ISoundTriggerHw2 {
+    /**
+     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#getProperties(android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback
+     */
+    android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Properties getProperties();
+
+    /**
+     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#loadSoundModel_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel,
+     * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int,
+     * android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback)
+     */
+    int loadSoundModel(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
+            SoundTriggerHw2Compat.Callback callback, int cookie);
+
+    /**
+     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#loadPhraseSoundModel_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel,
+     * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int,
+     * android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback)
+     */
+    int loadPhraseSoundModel(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
+            SoundTriggerHw2Compat.Callback callback, int cookie);
+
+    /**
+     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#unloadSoundModel(int)
+     */
+    void unloadSoundModel(int modelHandle);
+
+    /**
+     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#stopRecognition(int)
+     */
+    void stopRecognition(int modelHandle);
+
+    /**
+     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#stopAllRecognitions()
+     */
+    void stopAllRecognitions();
+
+    /**
+     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#startRecognition_2_1(int,
+     * android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig,
+     * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int)
+     */
+    void startRecognition(int modelHandle,
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config,
+            SoundTriggerHw2Compat.Callback callback, int cookie);
+
+    /**
+     * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#getModelState(int)
+     */
+    void getModelState(int modelHandle);
+
+    /**
+     * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#getParameter(int, int,
+     * ISoundTriggerHw.getParameterCallback)
+     */
+    int getModelParameter(int modelHandle, int param);
+
+    /**
+     * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#setParameter(int, int, int)
+     */
+    void setModelParameter(int modelHandle, int param, int value);
+
+    /**
+     * @return null if not supported.
+     * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#queryParameter(int, int,
+     * ISoundTriggerHw.queryParameterCallback)
+     */
+    ModelParameterRange queryParameter(int modelHandle, int param);
+
+    /**
+     * @see IHwBinder#linkToDeath(IHwBinder.DeathRecipient, long)
+     */
+    boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie);
+
+    /**
+     * @see IHwBinder#unlinkToDeath(IHwBinder.DeathRecipient)
+     */
+    boolean unlinkToDeath(IHwBinder.DeathRecipient recipient);
+
+    /**
+     * @see IBase#interfaceDescriptor()
+     */
+    String interfaceDescriptor() throws android.os.RemoteException;
+
+    interface Callback {
+        /**
+         * @see android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback#recognitionCallback_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent,
+         * int)
+         */
+        void recognitionCallback(
+                android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event,
+                int cookie);
+
+        /**
+         * @see android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback#phraseRecognitionCallback_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent,
+         * int)
+         */
+        void phraseRecognitionCallback(
+                android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent event,
+                int cookie);
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/InternalServerError.java b/services/core/java/com/android/server/soundtrigger_middleware/InternalServerError.java
new file mode 100644
index 0000000..e1fb226
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/InternalServerError.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+
+/**
+ * An internal server error.
+ * <p>
+ * This exception wraps any exception thrown from a service implementation, which is a result of a
+ * bug in the server implementation (or any of its dependencies).
+ * <p>
+ * Specifically, this type is excluded from the set of whitelisted exceptions that binder would
+ * tunnel to the client process, since these exceptions are ambiguous regarding whether the client
+ * had done something wrong or the server is buggy. For example, a client getting an
+ * IllegalArgumentException cannot easily determine whether they had provided illegal arguments to
+ * the method they were calling, or whether the method implementation provided illegal arguments to
+ * some method it was calling due to a bug.
+ *
+ * @hide
+ */
+public class InternalServerError extends RuntimeException {
+    public InternalServerError(@NonNull Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/RecoverableException.java b/services/core/java/com/android/server/soundtrigger_middleware/RecoverableException.java
new file mode 100644
index 0000000..8361850
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/RecoverableException.java
@@ -0,0 +1,53 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+
+/**
+ * This exception represents a fault which:
+ * <ul>
+ * <li>Could not have been anticipated by a caller (i.e. is not a violation of any preconditions).
+ * <li>Is guaranteed to not have been caused any meaningful state change in the callee. The caller
+ *     may continue operation as if the call has never been made.
+ * </ul>
+ * <p>
+ * Some recoverable faults are permanent and some are transient / circumstantial, the specific error
+ * code can provide more information about the possible recovery options.
+ * <p>
+ * The reason why this is a RuntimeException is to allow it to go through interfaces defined by
+ * AIDL, which we have no control over.
+ *
+ * @hide
+ */
+public class RecoverableException extends RuntimeException {
+    public final int errorCode;
+
+    public RecoverableException(int errorCode, @NonNull String message) {
+        super(message);
+        this.errorCode = errorCode;
+    }
+
+    public RecoverableException(int errorCode) {
+        this.errorCode = errorCode;
+    }
+
+    @Override
+    public @NonNull String toString() {
+        return super.toString() + " (code " + errorCode + ")";
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
new file mode 100644
index 0000000..4a852c4
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -0,0 +1,470 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.soundtrigger_middleware.Status;
+import android.os.IHwBinder;
+import android.os.RemoteException;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * An implementation of {@link ISoundTriggerHw2}, on top of any
+ * android.hardware.soundtrigger.V2_x.ISoundTriggerHw implementation. This class hides away some of
+ * the details involved with retaining backward compatibility and adapts to the more pleasant syntax
+ * exposed by {@link ISoundTriggerHw2}, compared to the bare driver interface.
+ * <p>
+ * Exception handling:
+ * <ul>
+ * <li>All {@link RemoteException}s get rethrown as {@link RuntimeException}.
+ * <li>All HAL malfunctions get thrown as {@link HalException}.
+ * <li>All unsupported operations get thrown as {@link RecoverableException} with a
+ * {@link android.media.soundtrigger_middleware.Status#OPERATION_NOT_SUPPORTED}
+ * code.
+ * </ul>
+ */
+final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
+    private final @NonNull
+    IHwBinder mBinder;
+    private final @NonNull
+    android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
+    private final @Nullable
+    android.hardware.soundtrigger.V2_1.ISoundTriggerHw mUnderlying_2_1;
+    private final @Nullable
+    android.hardware.soundtrigger.V2_2.ISoundTriggerHw mUnderlying_2_2;
+    private final @Nullable
+    android.hardware.soundtrigger.V2_3.ISoundTriggerHw mUnderlying_2_3;
+
+    public SoundTriggerHw2Compat(
+            @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw underlying) {
+        this(underlying.asBinder());
+    }
+
+    public SoundTriggerHw2Compat(IHwBinder binder) {
+        Objects.requireNonNull(binder);
+
+        mBinder = binder;
+
+        // We want to share the proxy instances rather than create a separate proxy for every
+        // version, so we go down the versions in descending order to find the latest one supported,
+        // and then simply up-cast it to obtain all the versions that are earlier.
+
+        // Attempt 2.3
+        android.hardware.soundtrigger.V2_3.ISoundTriggerHw as2_3 =
+                android.hardware.soundtrigger.V2_3.ISoundTriggerHw.asInterface(binder);
+        if (as2_3 != null) {
+            mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = as2_3;
+            return;
+        }
+
+        // Attempt 2.2
+        android.hardware.soundtrigger.V2_2.ISoundTriggerHw as2_2 =
+                android.hardware.soundtrigger.V2_2.ISoundTriggerHw.asInterface(binder);
+        if (as2_2 != null) {
+            mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = as2_2;
+            mUnderlying_2_3 = null;
+            return;
+        }
+
+        // Attempt 2.1
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw as2_1 =
+                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.asInterface(binder);
+        if (as2_1 != null) {
+            mUnderlying_2_0 = mUnderlying_2_1 = as2_1;
+            mUnderlying_2_2 = mUnderlying_2_3 = null;
+            return;
+        }
+
+        // Attempt 2.0
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHw as2_0 =
+                android.hardware.soundtrigger.V2_0.ISoundTriggerHw.asInterface(binder);
+        if (as2_0 != null) {
+            mUnderlying_2_0 = as2_0;
+            mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = null;
+            return;
+        }
+
+        throw new RuntimeException("Binder doesn't support ISoundTriggerHw@2.0");
+    }
+
+    private static void handleHalStatus(int status, String methodName) {
+        if (status != 0) {
+            throw new HalException(status, methodName);
+        }
+    }
+
+    @Override
+    public android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Properties getProperties() {
+        try {
+            AtomicInteger retval = new AtomicInteger(-1);
+            AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Properties>
+                    properties =
+                    new AtomicReference<>();
+            as2_0().getProperties(
+                    (r, p) -> {
+                        retval.set(r);
+                        properties.set(p);
+                    });
+            handleHalStatus(retval.get(), "getProperties");
+            return properties.get();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public int loadSoundModel(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
+            Callback callback, int cookie) {
+        try {
+            AtomicInteger retval = new AtomicInteger(-1);
+            AtomicInteger handle = new AtomicInteger(0);
+            try {
+                as2_1().loadSoundModel_2_1(soundModel, new SoundTriggerCallback(callback), cookie,
+                        (r, h) -> {
+                            retval.set(r);
+                            handle.set(h);
+                        });
+            } catch (NotSupported e) {
+                // Fall-back to the 2.0 version:
+                return loadSoundModel_2_0(soundModel, callback, cookie);
+            }
+            handleHalStatus(retval.get(), "loadSoundModel_2_1");
+            return handle.get();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public int loadPhraseSoundModel(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
+            Callback callback, int cookie) {
+        try {
+            AtomicInteger retval = new AtomicInteger(-1);
+            AtomicInteger handle = new AtomicInteger(0);
+            try {
+                as2_1().loadPhraseSoundModel_2_1(soundModel, new SoundTriggerCallback(callback),
+                        cookie,
+                        (r, h) -> {
+                            retval.set(r);
+                            handle.set(h);
+                        });
+            } catch (NotSupported e) {
+                // Fall-back to the 2.0 version:
+                return loadPhraseSoundModel_2_0(soundModel, callback, cookie);
+            }
+            handleHalStatus(retval.get(), "loadSoundModel_2_1");
+            return handle.get();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public void unloadSoundModel(int modelHandle) {
+        try {
+            int retval = as2_0().unloadSoundModel(modelHandle);
+            handleHalStatus(retval, "unloadSoundModel");
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public void stopRecognition(int modelHandle) {
+        try {
+            int retval = as2_0().stopRecognition(modelHandle);
+            handleHalStatus(retval, "stopRecognition");
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+
+    }
+
+    @Override
+    public void stopAllRecognitions() {
+        try {
+            int retval = as2_0().stopAllRecognitions();
+            handleHalStatus(retval, "stopAllRecognitions");
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public void startRecognition(int modelHandle,
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config,
+            Callback callback, int cookie) {
+        try {
+            try {
+                int retval = as2_1().startRecognition_2_1(modelHandle, config,
+                        new SoundTriggerCallback(callback), cookie);
+                handleHalStatus(retval, "startRecognition_2_1");
+            } catch (NotSupported e) {
+                // Fall-back to the 2.0 version:
+                startRecognition_2_0(modelHandle, config, callback, cookie);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
+    public void getModelState(int modelHandle) {
+        try {
+            int retval = as2_2().getModelState(modelHandle);
+            handleHalStatus(retval, "getModelState");
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        } catch (NotSupported e) {
+            throw e.throwAsRecoverableException();
+        }
+    }
+
+    @Override
+    public int getModelParameter(int modelHandle, int param) {
+        AtomicInteger status = new AtomicInteger(-1);
+        AtomicInteger value = new AtomicInteger(0);
+        try {
+            as2_3().getParameter(modelHandle, param,
+                    (s, v) -> {
+                        status.set(s);
+                        value.set(v);
+                    });
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        } catch (NotSupported e) {
+            throw e.throwAsRecoverableException();
+        }
+        handleHalStatus(status.get(), "getParameter");
+        return value.get();
+    }
+
+    @Override
+    public void setModelParameter(int modelHandle, int param, int value) {
+        try {
+            int retval = as2_3().setParameter(modelHandle, param, value);
+            handleHalStatus(retval, "setParameter");
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        } catch (NotSupported e) {
+            throw e.throwAsRecoverableException();
+        }
+    }
+
+    @Override
+    public android.hardware.soundtrigger.V2_3.ModelParameterRange queryParameter(int modelHandle,
+            int param) {
+        AtomicInteger status = new AtomicInteger(-1);
+        AtomicReference<android.hardware.soundtrigger.V2_3.OptionalModelParameterRange>
+                optionalRange =
+                new AtomicReference<>();
+        try {
+            as2_3().queryParameter(modelHandle, param,
+                    (s, r) -> {
+                        status.set(s);
+                        optionalRange.set(r);
+                    });
+        } catch (NotSupported e) {
+            // For older drivers, we consider no model parameter to be supported.
+            return null;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+        handleHalStatus(status.get(), "queryParameter");
+        return (optionalRange.get().getDiscriminator()
+                == android.hardware.soundtrigger.V2_3.OptionalModelParameterRange.hidl_discriminator.range)
+                ?
+                optionalRange.get().range() : null;
+    }
+
+    @Override
+    public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
+        return mBinder.linkToDeath(recipient, cookie);
+    }
+
+    @Override
+    public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
+        return mBinder.unlinkToDeath(recipient);
+    }
+
+    @Override
+    public String interfaceDescriptor() throws RemoteException {
+        return as2_0().interfaceDescriptor();
+    }
+
+    private int loadSoundModel_2_0(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
+            Callback callback, int cookie)
+            throws RemoteException {
+        // Convert the soundModel to V2.0.
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model_2_0 =
+                Hw2CompatUtil.convertSoundModel_2_1_to_2_0(soundModel);
+
+        AtomicInteger retval = new AtomicInteger(-1);
+        AtomicInteger handle = new AtomicInteger(0);
+        as2_0().loadSoundModel(model_2_0, new SoundTriggerCallback(callback), cookie, (r, h) -> {
+            retval.set(r);
+            handle.set(h);
+        });
+        handleHalStatus(retval.get(), "loadSoundModel");
+        return handle.get();
+    }
+
+    private int loadPhraseSoundModel_2_0(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
+            Callback callback, int cookie)
+            throws RemoteException {
+        // Convert the soundModel to V2.0.
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model_2_0 =
+                Hw2CompatUtil.convertPhraseSoundModel_2_1_to_2_0(soundModel);
+
+        AtomicInteger retval = new AtomicInteger(-1);
+        AtomicInteger handle = new AtomicInteger(0);
+        as2_0().loadPhraseSoundModel(model_2_0, new SoundTriggerCallback(callback), cookie,
+                (r, h) -> {
+                    retval.set(r);
+                    handle.set(h);
+                });
+        handleHalStatus(retval.get(), "loadSoundModel");
+        return handle.get();
+    }
+
+    private void startRecognition_2_0(int modelHandle,
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config,
+            Callback callback, int cookie)
+            throws RemoteException {
+
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config_2_0 =
+                Hw2CompatUtil.convertRecognitionConfig_2_1_to_2_0(config);
+        int retval = as2_0().startRecognition(modelHandle, config_2_0,
+                new SoundTriggerCallback(callback), cookie);
+        handleHalStatus(retval, "startRecognition");
+    }
+
+    private @NonNull
+    android.hardware.soundtrigger.V2_0.ISoundTriggerHw as2_0() {
+        return mUnderlying_2_0;
+    }
+
+    private @NonNull
+    android.hardware.soundtrigger.V2_1.ISoundTriggerHw as2_1() throws NotSupported {
+        if (mUnderlying_2_1 == null) {
+            throw new NotSupported("Underlying driver version < 2.1");
+        }
+        return mUnderlying_2_1;
+    }
+
+    private @NonNull
+    android.hardware.soundtrigger.V2_2.ISoundTriggerHw as2_2() throws NotSupported {
+        if (mUnderlying_2_2 == null) {
+            throw new NotSupported("Underlying driver version < 2.2");
+        }
+        return mUnderlying_2_2;
+    }
+
+    private @NonNull
+    android.hardware.soundtrigger.V2_3.ISoundTriggerHw as2_3() throws NotSupported {
+        if (mUnderlying_2_3 == null) {
+            throw new NotSupported("Underlying driver version < 2.3");
+        }
+        return mUnderlying_2_3;
+    }
+
+    /**
+     * A checked exception representing the requested interface version not being supported.
+     * At the public interface layer, use {@link #throwAsRecoverableException()} to propagate it to
+     * the caller if the request cannot be fulfilled.
+     */
+    private static class NotSupported extends Exception {
+        NotSupported(String message) {
+            super(message);
+        }
+
+        /**
+         * Throw this as a recoverable exception.
+         *
+         * @return Never actually returns anything. Always throws. Used so that caller can write
+         * throw e.throwAsRecoverableException().
+         */
+        RecoverableException throwAsRecoverableException() {
+            throw new RecoverableException(Status.OPERATION_NOT_SUPPORTED, getMessage());
+        }
+    }
+
+    private static class SoundTriggerCallback extends
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.Stub {
+        private final @NonNull
+        Callback mDelegate;
+
+        private SoundTriggerCallback(
+                @NonNull Callback delegate) {
+            mDelegate = Objects.requireNonNull(delegate);
+        }
+
+        @Override
+        public void recognitionCallback_2_1(
+                android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event,
+                int cookie) {
+            mDelegate.recognitionCallback(event, cookie);
+        }
+
+        @Override
+        public void phraseRecognitionCallback_2_1(
+                android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent event,
+                int cookie) {
+            mDelegate.phraseRecognitionCallback(event, cookie);
+        }
+
+        @Override
+        public void soundModelCallback_2_1(
+                android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent event,
+                int cookie) {
+            // Nobody cares.
+        }
+
+        @Override
+        public void recognitionCallback(
+                android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent event,
+                int cookie) {
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event_2_1 =
+                    Hw2CompatUtil.convertRecognitionEvent_2_0_to_2_1(event);
+            mDelegate.recognitionCallback(event_2_1, cookie);
+        }
+
+        @Override
+        public void phraseRecognitionCallback(
+                android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent event,
+                int cookie) {
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent
+                    event_2_1 = Hw2CompatUtil.convertPhraseRecognitionEvent_2_0_to_2_1(event);
+            mDelegate.phraseRecognitionCallback(event_2_1, cookie);
+        }
+
+        @Override
+        public void soundModelCallback(
+                android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent event,
+                int cookie) {
+            // Nobody cares.
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
new file mode 100644
index 0000000..9d51b65
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
@@ -0,0 +1,140 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.media.soundtrigger_middleware.ISoundTriggerCallback;
+import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
+import android.media.soundtrigger_middleware.ISoundTriggerModule;
+import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is an implementation of the ISoundTriggerMiddlewareService interface.
+ * <p>
+ * <b>Important conventions:</b>
+ * <ul>
+ * <li>Correct usage is assumed. This implementation does not attempt to gracefully handle invalid
+ * usage, and such usage will result in undefined behavior. If this service is to be offered to an
+ * untrusted client, it must be wrapped with input and state validation.
+ * <li>There is no binder instance associated with this implementation. Do not call asBinder().
+ * <li>The implementation may throw a {@link RecoverableException} to indicate non-fatal,
+ * recoverable faults. The error code would one of the
+ * {@link android.media.soundtrigger_middleware.Status}
+ * constants. Any other exception thrown should be regarded as a bug in the implementation or one
+ * of its dependencies (assuming correct usage).
+ * <li>The implementation is designed for testibility by featuring dependency injection (the
+ * underlying HAL driver instances are passed to the ctor) and by minimizing dependencies on
+ * Android runtime.
+ * <li>The implementation is thread-safe.
+ * </ul>
+ *
+ * @hide
+ */
+public class SoundTriggerMiddlewareImpl implements ISoundTriggerMiddlewareService {
+    static private final String TAG = "SoundTriggerMiddlewareImpl";
+    private final SoundTriggerModule[] mModules;
+
+    /**
+     * Interface to the audio system, which can allocate capture session handles.
+     * SoundTrigger uses those sessions in order to associate a recognition session with an optional
+     * capture from the same device that triggered the recognition.
+     */
+    public static abstract class AudioSessionProvider {
+        public static final class AudioSession {
+            final int mSessionHandle;
+            final int mIoHandle;
+            final int mDeviceHandle;
+
+            AudioSession(int sessionHandle, int ioHandle, int deviceHandle) {
+                mSessionHandle = sessionHandle;
+                mIoHandle = ioHandle;
+                mDeviceHandle = deviceHandle;
+            }
+        }
+
+        public abstract AudioSession acquireSession();
+
+        public abstract void releaseSession(int sessionHandle);
+    }
+
+    /**
+     * Most generic constructor - gets an array of HAL driver instances.
+     */
+    public SoundTriggerMiddlewareImpl(@NonNull ISoundTriggerHw[] halServices,
+            @NonNull AudioSessionProvider audioSessionProvider) {
+        List<SoundTriggerModule> modules = new ArrayList<>(halServices.length);
+
+        for (int i = 0; i < halServices.length; ++i) {
+            ISoundTriggerHw service = halServices[i];
+            try {
+                modules.add(new SoundTriggerModule(service, audioSessionProvider));
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to a SoundTriggerModule instance", e);
+            }
+        }
+
+        mModules = modules.toArray(new SoundTriggerModule[modules.size()]);
+    }
+
+    /**
+     * Convenience constructor - gets a single HAL driver instance.
+     */
+    public SoundTriggerMiddlewareImpl(@NonNull ISoundTriggerHw halService,
+            @NonNull AudioSessionProvider audioSessionProvider) {
+        this(new ISoundTriggerHw[]{halService}, audioSessionProvider);
+    }
+
+    @Override
+    public @NonNull
+    SoundTriggerModuleDescriptor[] listModules() {
+        SoundTriggerModuleDescriptor[] result = new SoundTriggerModuleDescriptor[mModules.length];
+
+        for (int i = 0; i < mModules.length; ++i) {
+            SoundTriggerModuleDescriptor desc = new SoundTriggerModuleDescriptor();
+            desc.handle = i;
+            desc.properties = mModules[i].getProperties();
+            result[i] = desc;
+        }
+        return result;
+    }
+
+    @Override
+    public @NonNull
+    ISoundTriggerModule attach(int handle, @NonNull ISoundTriggerCallback callback) {
+        return mModules[handle].attach(callback);
+    }
+
+    @Override
+    public void setExternalCaptureState(boolean active) {
+        for (SoundTriggerModule module : mModules) {
+            module.setExternalCaptureState(active);
+        }
+    }
+
+    @Override
+    public @NonNull
+    IBinder asBinder() {
+        throw new UnsupportedOperationException(
+                "This implementation is not inteded to be used directly with Binder.");
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
new file mode 100644
index 0000000..a7cfe10
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -0,0 +1,709 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.media.soundtrigger_middleware.ISoundTriggerCallback;
+import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
+import android.media.soundtrigger_middleware.ISoundTriggerModule;
+import android.media.soundtrigger_middleware.ModelParameterRange;
+import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger_middleware.PhraseSoundModel;
+import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger_middleware.RecognitionStatus;
+import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.SystemService;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This is a wrapper around an {@link ISoundTriggerMiddlewareService} implementation, which exposes
+ * it as a Binder service and enforces permissions and correct usage by the client, as well as makes
+ * sure that exceptions representing a server malfunction do not get sent to the client.
+ * <p>
+ * This is intended to extract the non-business logic out of the underlying implementation and thus
+ * make it easier to maintain each one of those separate aspects. A design trade-off is being made
+ * here, in that this class would need to essentially eavesdrop on all the client-server
+ * communication and retain all state known to the client, while the client doesn't necessarily care
+ * about all of it, and while the server has its own representation of this information. However,
+ * in this case, this is a small amount of data, and the benefits in code elegance seem worth it.
+ * There is also some additional cost in employing a simplistic locking mechanism here, but
+ * following the same line of reasoning, the benefits in code simplicity outweigh it.
+ * <p>
+ * Every public method in this class, overriding an interface method, must follow the following
+ * pattern:
+ * <code><pre>
+ * @Override public T method(S arg) {
+ *     // Permission check.
+ *     checkPermissions();
+ *     // Input validation.
+ *     ValidationUtil.validateS(arg);
+ *     synchronized (this) {
+ *         // State validation.
+ *         if (...state is not valid for this call...) {
+ *             throw new IllegalStateException("State is invalid because...");
+ *         }
+ *         // From here on, every exception isn't client's fault.
+ *         try {
+ *             T result = mDelegate.method(arg);
+ *             // Update state.;
+ *             ...
+ *             return result;
+ *         } catch (Exception e) {
+ *             throw handleException(e);
+ *         }
+ *     }
+ * }
+ * </pre></code>
+ * Following this patterns ensures a consistent and rigorous handling of all aspects associated
+ * with client-server separation.
+ * <p>
+ * <b>Exception handling approach:</b><br>
+ * We make sure all client faults (permissions, argument and state validation) happen first, and
+ * would throw {@link SecurityException}, {@link IllegalArgumentException}/
+ * {@link NullPointerException} or {@link
+ * IllegalStateException}, respectively. All those exceptions are treated specially by Binder and
+ * will get sent back to the client.<br>
+ * Once this is done, any subsequent fault is considered a server fault. Only {@link
+ * RecoverableException}s thrown by the implementation are special-cased: they would get sent back
+ * to the caller as a {@link ServiceSpecificException}, which is the behavior of Binder. Any other
+ * exception gets wrapped with a {@link InternalServerError}, which is specifically chosen as a type
+ * that <b>does NOT</b> get forwarded by binder. Those exceptions would be handled by a high-level
+ * exception handler on the server side, typically resulting in rebooting the server.
+ * <p>
+ * <b>Exposing this service as a System Service:</b><br>
+ * Insert this line into {@link com.android.server.SystemServer}:
+ * <code><pre>
+ * mSystemServiceManager.startService(SoundTriggerMiddlewareService.Lifecycle.class);
+ * </pre></code>
+ *
+ * {@hide}
+ */
+public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareService.Stub {
+    static private final String TAG = "SoundTriggerMiddlewareService";
+
+    final ISoundTriggerMiddlewareService mDelegate;
+    final Context mContext;
+    Set<Integer> mModuleHandles;
+
+    /**
+     * Constructor for internal use only. Could be exposed for testing purposes in the future.
+     * Users should access this class via {@link Lifecycle}.
+     */
+    private SoundTriggerMiddlewareService(
+            @NonNull ISoundTriggerMiddlewareService delegate, @NonNull Context context) {
+        mDelegate = delegate;
+        mContext = context;
+    }
+
+    /**
+     * Generic exception handling for exceptions thrown by the underlying implementation.
+     *
+     * Would throw any {@link RecoverableException} as a {@link ServiceSpecificException} (passed
+     * by Binder to the caller) and <i>any other</i> exception as {@link InternalServerError}
+     * (<b>not</b> passed by Binder to the caller).
+     * <p>
+     * Typical usage:
+     * <code><pre>
+     * try {
+     *     ... Do server operations ...
+     * } catch (Exception e) {
+     *     throw handleException(e);
+     * }
+     * </pre></code>
+     */
+    private static @NonNull
+    RuntimeException handleException(@NonNull Exception e) {
+        if (e instanceof RecoverableException) {
+            throw new ServiceSpecificException(((RecoverableException) e).errorCode,
+                    e.getMessage());
+        }
+        throw new InternalServerError(e);
+    }
+
+    @Override
+    public @NonNull
+    SoundTriggerModuleDescriptor[] listModules() {
+        // Permission check.
+        checkPermissions();
+        // Input validation (always valid).
+
+        synchronized (this) {
+            // State validation (always valid).
+
+            // From here on, every exception isn't client's fault.
+            try {
+                SoundTriggerModuleDescriptor[] result = mDelegate.listModules();
+                mModuleHandles = new HashSet<>(result.length);
+                for (SoundTriggerModuleDescriptor desc : result) {
+                    mModuleHandles.add(desc.handle);
+                }
+                return result;
+            } catch (Exception e) {
+                throw handleException(e);
+            }
+        }
+    }
+
+    @Override
+    public @NonNull
+    ISoundTriggerModule attach(int handle, @NonNull ISoundTriggerCallback callback) {
+        // Permission check.
+        checkPermissions();
+        // Input validation.
+        Preconditions.checkNotNull(callback);
+        Preconditions.checkNotNull(callback.asBinder());
+
+        synchronized (this) {
+            // State validation.
+            if (mModuleHandles == null) {
+                throw new IllegalStateException(
+                        "Client must call listModules() prior to attaching.");
+            }
+            if (!mModuleHandles.contains(handle)) {
+                throw new IllegalArgumentException("Invalid handle: " + handle);
+            }
+
+            // From here on, every exception isn't client's fault.
+            try {
+                ModuleService moduleService = new ModuleService(callback);
+                moduleService.attach(mDelegate.attach(handle, moduleService));
+                return moduleService;
+            } catch (Exception e) {
+                throw handleException(e);
+            }
+        }
+    }
+
+    @Override
+    public void setExternalCaptureState(boolean active) {
+        // Permission check.
+        checkPreemptPermissions();
+        // Input validation (always valid).
+
+        synchronized (this) {
+            // State validation (always valid).
+
+            // From here on, every exception isn't client's fault.
+            try {
+                mDelegate.setExternalCaptureState(active);
+            } catch (Exception e) {
+                throw handleException(e);
+            }
+        }
+    }
+
+    /**
+     * Throws a {@link SecurityException} if caller doesn't have the right permissions to use this
+     * service.
+     */
+    private void checkPermissions() {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.RECORD_AUDIO,
+                "Caller must have the android.permission.RECORD_AUDIO permission.");
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD,
+                "Caller must have the android.permission.CAPTURE_AUDIO_HOTWORD permission.");
+    }
+
+    /**
+     * Throws a {@link SecurityException} if caller doesn't have the right permissions to preempt
+     * active sound trigger sessions.
+     */
+    private void checkPreemptPermissions() {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.PREEMPT_SOUND_TRIGGER,
+                "Caller must have the android.permission.PREEMPT_SOUND_TRIGGER permission.");
+    }
+
+    /** State of a sound model. */
+    static class ModelState {
+        /** Activity state of a sound model. */
+        enum Activity {
+            /** Model is loaded, recognition is inactive. */
+            LOADED,
+            /** Model is loaded, recognition is active. */
+            ACTIVE
+        }
+
+        /** Activity state. */
+        public Activity activityState = Activity.LOADED;
+
+        /**
+         * A map of known parameter support. A missing key means we don't know yet whether the
+         * parameter is supported. A null value means it is known to not be supported. A non-null
+         * value indicates the valid value range.
+         */
+        private Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
+
+        /**
+         * Check that the given parameter is known to be supported for this model.
+         *
+         * @param modelParam The parameter key.
+         */
+        public void checkSupported(int modelParam) {
+            if (!parameterSupport.containsKey(modelParam)) {
+                throw new IllegalStateException("Parameter has not been checked for support.");
+            }
+            ModelParameterRange range = parameterSupport.get(modelParam);
+            if (range == null) {
+                throw new IllegalArgumentException("Paramater is not supported.");
+            }
+        }
+
+        /**
+         * Check that the given parameter is known to be supported for this model and that the given
+         * value is a valid value for it.
+         *
+         * @param modelParam The parameter key.
+         * @param value      The value.
+         */
+        public void checkSupported(int modelParam, int value) {
+            if (!parameterSupport.containsKey(modelParam)) {
+                throw new IllegalStateException("Parameter has not been checked for support.");
+            }
+            ModelParameterRange range = parameterSupport.get(modelParam);
+            if (range == null) {
+                throw new IllegalArgumentException("Paramater is not supported.");
+            }
+            Preconditions.checkArgumentInRange(value, range.minInclusive, range.maxInclusive,
+                    "value");
+        }
+
+        /**
+         * Update support state for the given parameter for this model.
+         *
+         * @param modelParam The parameter key.
+         * @param range      The parameter value range, or null if not supported.
+         */
+        public void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
+            parameterSupport.put(modelParam, range);
+        }
+    }
+
+    /**
+     * Entry-point to this module: exposes the module as a {@link SystemService}.
+     */
+    public static final class Lifecycle extends SystemService {
+        private SoundTriggerMiddlewareService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            ISoundTriggerHw[] services;
+            try {
+                services = new ISoundTriggerHw[]{ISoundTriggerHw.getService(true)};
+                Log.d(TAG, "Connected to default ISoundTriggerHw");
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to connect to default ISoundTriggerHw", e);
+                services = new ISoundTriggerHw[0];
+            }
+
+            mService = new SoundTriggerMiddlewareService(
+                    new SoundTriggerMiddlewareImpl(services, new AudioSessionProviderImpl()),
+                    getContext());
+            publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE, mService);
+        }
+    }
+
+    /**
+     * A wrapper around an {@link ISoundTriggerModule} implementation, to address the same aspects
+     * mentioned in {@link SoundTriggerModule} above. This class follows the same conventions.
+     */
+    private class ModuleService extends ISoundTriggerModule.Stub implements ISoundTriggerCallback,
+            DeathRecipient {
+        private final ISoundTriggerCallback mCallback;
+        private ISoundTriggerModule mDelegate;
+        private Map<Integer, ModelState> mLoadedModels = new HashMap<>();
+
+        ModuleService(@NonNull ISoundTriggerCallback callback) {
+            mCallback = callback;
+            try {
+                mCallback.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+        }
+
+        void attach(@NonNull ISoundTriggerModule delegate) {
+            mDelegate = delegate;
+        }
+
+        @Override
+        public int loadModel(@NonNull SoundModel model) {
+            // Permission check.
+            checkPermissions();
+            // Input validation.
+            ValidationUtil.validateGenericModel(model);
+
+            synchronized (this) {
+                // State validation.
+                if (mDelegate == null) {
+                    throw new IllegalStateException("Module has been detached.");
+                }
+
+                // From here on, every exception isn't client's fault.
+                try {
+                    int handle = mDelegate.loadModel(model);
+                    mLoadedModels.put(handle, new ModelState());
+                    return handle;
+                } catch (Exception e) {
+                    throw handleException(e);
+                }
+            }
+        }
+
+        @Override
+        public int loadPhraseModel(@NonNull PhraseSoundModel model) {
+            // Permission check.
+            checkPermissions();
+            // Input validation.
+            ValidationUtil.validatePhraseModel(model);
+
+            synchronized (this) {
+                // State validation.
+                if (mDelegate == null) {
+                    throw new IllegalStateException("Module has been detached.");
+                }
+
+                // From here on, every exception isn't client's fault.
+                try {
+                    int handle = mDelegate.loadPhraseModel(model);
+                    mLoadedModels.put(handle, new ModelState());
+                    return handle;
+                } catch (Exception e) {
+                    throw handleException(e);
+                }
+            }
+        }
+
+        @Override
+        public void unloadModel(int modelHandle) {
+            // Permission check.
+            checkPermissions();
+            // Input validation (always valid).
+
+            synchronized (this) {
+                // State validation.
+                if (mDelegate == null) {
+                    throw new IllegalStateException("Module has been detached.");
+                }
+                ModelState modelState = mLoadedModels.get(modelHandle);
+                if (modelState == null) {
+                    throw new IllegalStateException("Invalid handle: " + modelHandle);
+                }
+                if (modelState.activityState != ModelState.Activity.LOADED) {
+                    throw new IllegalStateException("Model with handle: " + modelHandle
+                            + " has invalid state for unloading: " + modelState.activityState);
+                }
+
+                // From here on, every exception isn't client's fault.
+                try {
+                    mDelegate.unloadModel(modelHandle);
+                    mLoadedModels.remove(modelHandle);
+                } catch (Exception e) {
+                    throw handleException(e);
+                }
+            }
+        }
+
+        @Override
+        public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
+            // Permission check.
+            checkPermissions();
+            // Input validation.
+            ValidationUtil.validateRecognitionConfig(config);
+
+            synchronized (this) {
+                // State validation.
+                if (mDelegate == null) {
+                    throw new IllegalStateException("Module has been detached.");
+                }
+                ModelState modelState = mLoadedModels.get(modelHandle);
+                if (modelState == null) {
+                    throw new IllegalStateException("Invalid handle: " + modelHandle);
+                }
+                if (modelState.activityState != ModelState.Activity.LOADED) {
+                    throw new IllegalStateException("Model with handle: " + modelHandle
+                            + " has invalid state for starting recognition: "
+                            + modelState.activityState);
+                }
+
+                // From here on, every exception isn't client's fault.
+                try {
+                    mDelegate.startRecognition(modelHandle, config);
+                    modelState.activityState = ModelState.Activity.ACTIVE;
+                } catch (Exception e) {
+                    throw handleException(e);
+                }
+            }
+        }
+
+        @Override
+        public void stopRecognition(int modelHandle) {
+            // Permission check.
+            checkPermissions();
+            // Input validation (always valid).
+
+            synchronized (this) {
+                // State validation.
+                if (mDelegate == null) {
+                    throw new IllegalStateException("Module has been detached.");
+                }
+                ModelState modelState = mLoadedModels.get(modelHandle);
+                if (modelState == null) {
+                    throw new IllegalStateException("Invalid handle: " + modelHandle);
+                }
+                // stopRecognition is idempotent - no need to check model state.
+
+                // From here on, every exception isn't client's fault.
+                try {
+                    mDelegate.stopRecognition(modelHandle);
+                    modelState.activityState = ModelState.Activity.LOADED;
+                } catch (Exception e) {
+                    throw handleException(e);
+                }
+            }
+        }
+
+        @Override
+        public void forceRecognitionEvent(int modelHandle) {
+            // Permission check.
+            checkPermissions();
+            // Input validation (always valid).
+
+            synchronized (this) {
+                // State validation.
+                if (mDelegate == null) {
+                    throw new IllegalStateException("Module has been detached.");
+                }
+                ModelState modelState = mLoadedModels.get(modelHandle);
+                if (modelState == null) {
+                    throw new IllegalStateException("Invalid handle: " + modelHandle);
+                }
+                // forceRecognitionEvent is idempotent - no need to check model state.
+
+                // From here on, every exception isn't client's fault.
+                try {
+                    mDelegate.forceRecognitionEvent(modelHandle);
+                } catch (Exception e) {
+                    throw handleException(e);
+                }
+            }
+        }
+
+        @Override
+        public void setModelParameter(int modelHandle, int modelParam, int value) {
+            // Permission check.
+            checkPermissions();
+            // Input validation.
+            ValidationUtil.validateModelParameter(modelParam);
+
+            synchronized (this) {
+                // State validation.
+                if (mDelegate == null) {
+                    throw new IllegalStateException("Module has been detached.");
+                }
+                ModelState modelState = mLoadedModels.get(modelHandle);
+                if (modelState == null) {
+                    throw new IllegalStateException("Invalid handle: " + modelHandle);
+                }
+                modelState.checkSupported(modelParam, value);
+
+                // From here on, every exception isn't client's fault.
+                try {
+                    mDelegate.setModelParameter(modelHandle, modelParam, value);
+                } catch (Exception e) {
+                    throw handleException(e);
+                }
+            }
+        }
+
+        @Override
+        public int getModelParameter(int modelHandle, int modelParam) {
+            // Permission check.
+            checkPermissions();
+            // Input validation.
+            ValidationUtil.validateModelParameter(modelParam);
+
+            synchronized (this) {
+                // State validation.
+                if (mDelegate == null) {
+                    throw new IllegalStateException("Module has been detached.");
+                }
+                ModelState modelState = mLoadedModels.get(modelHandle);
+                if (modelState == null) {
+                    throw new IllegalStateException("Invalid handle: " + modelHandle);
+                }
+                modelState.checkSupported(modelParam);
+
+                // From here on, every exception isn't client's fault.
+                try {
+                    return mDelegate.getModelParameter(modelHandle, modelParam);
+                } catch (Exception e) {
+                    throw handleException(e);
+                }
+            }
+        }
+
+        @Override
+        @Nullable
+        public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam) {
+            // Permission check.
+            checkPermissions();
+            // Input validation.
+            ValidationUtil.validateModelParameter(modelParam);
+
+            synchronized (this) {
+                // State validation.
+                if (mDelegate == null) {
+                    throw new IllegalStateException("Module has been detached.");
+                }
+                ModelState modelState = mLoadedModels.get(modelHandle);
+                if (modelState == null) {
+                    throw new IllegalStateException("Invalid handle: " + modelHandle);
+                }
+
+                // From here on, every exception isn't client's fault.
+                try {
+                    ModelParameterRange result = mDelegate.queryModelParameterSupport(modelHandle,
+                            modelParam);
+                    modelState.updateParameterSupport(modelParam, result);
+                    return result;
+                } catch (Exception e) {
+                    throw handleException(e);
+                }
+            }
+        }
+
+        @Override
+        public void detach() {
+            // Permission check.
+            checkPermissions();
+            // Input validation (always valid).
+
+            synchronized (this) {
+                // State validation.
+                if (mDelegate == null) {
+                    throw new IllegalStateException("Module has already been detached.");
+                }
+                if (!mLoadedModels.isEmpty()) {
+                    throw new IllegalStateException("Cannot detach while models are loaded.");
+                }
+
+                // From here on, every exception isn't client's fault.
+                try {
+                    detachInternal();
+                } catch (Exception e) {
+                    throw handleException(e);
+                }
+            }
+        }
+
+        private void detachInternal() {
+            try {
+                mDelegate.detach();
+                mDelegate = null;
+                mCallback.asBinder().unlinkToDeath(this, 0);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+        }
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // Callbacks
+
+        @Override
+        public void onRecognition(int modelHandle, @NonNull RecognitionEvent event) {
+            synchronized (this) {
+                if (event.status != RecognitionStatus.FORCED) {
+                    mLoadedModels.get(modelHandle).activityState = ModelState.Activity.LOADED;
+                }
+                try {
+                    mCallback.onRecognition(modelHandle, event);
+                } catch (RemoteException e) {
+                    // Dead client will be handled by binderDied() - no need to handle here.
+                    // In any case, client callbacks are considered best effort.
+                    Log.e(TAG, "Client callback execption.", e);
+                }
+            }
+        }
+
+        @Override
+        public void onPhraseRecognition(int modelHandle, @NonNull PhraseRecognitionEvent event) {
+            synchronized (this) {
+                if (event.common.status != RecognitionStatus.FORCED) {
+                    mLoadedModels.get(modelHandle).activityState = ModelState.Activity.LOADED;
+                }
+                try {
+                    mCallback.onPhraseRecognition(modelHandle, event);
+                } catch (RemoteException e) {
+                    // Dead client will be handled by binderDied() - no need to handle here.
+                    // In any case, client callbacks are considered best effort.
+                    Log.e(TAG, "Client callback execption.", e);
+                }
+            }
+        }
+
+        @Override
+        public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
+            synchronized (this) {
+                try {
+                    mCallback.onRecognitionAvailabilityChange(available);
+                } catch (RemoteException e) {
+                    // Dead client will be handled by binderDied() - no need to handle here.
+                    // In any case, client callbacks are considered best effort.
+                    Log.e(TAG, "Client callback execption.", e);
+                }
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            // This is called whenever our client process dies.
+            synchronized (this) {
+                try {
+                    // Gracefully stop all active recognitions and unload the models.
+                    for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
+                        if (entry.getValue().activityState == ModelState.Activity.ACTIVE) {
+                            mDelegate.stopRecognition(entry.getKey());
+                        }
+                        mDelegate.unloadModel(entry.getKey());
+                    }
+                    // Detach.
+                    detachInternal();
+                } catch (Exception e) {
+                    throw handleException(e);
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
new file mode 100644
index 0000000..81789e1
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -0,0 +1,545 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
+import android.hardware.soundtrigger.V2_2.ISoundTriggerHw;
+import android.media.soundtrigger_middleware.ISoundTriggerCallback;
+import android.media.soundtrigger_middleware.ISoundTriggerModule;
+import android.media.soundtrigger_middleware.ModelParameterRange;
+import android.media.soundtrigger_middleware.PhraseSoundModel;
+import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger_middleware.SoundModelType;
+import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
+import android.media.soundtrigger_middleware.Status;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This is an implementation of a single module of the ISoundTriggerMiddlewareService interface,
+ * exposing itself through the {@link ISoundTriggerModule} interface, possibly to multiple separate
+ * clients.
+ * <p>
+ * Typical usage is to query the module capabilities using {@link #getProperties()} and then to use
+ * the module through an {@link ISoundTriggerModule} instance, obtained via {@link
+ * #attach(ISoundTriggerCallback)}. Every such interface is its own session and state is not shared
+ * between sessions (i.e. cannot use a handle obtained from one session through another).
+ * <p>
+ * <b>Important conventions:</b>
+ * <ul>
+ * <li>Correct usage is assumed. This implementation does not attempt to gracefully handle
+ * invalid usage, and such usage will result in undefined behavior. If this service is to be
+ * offered to an untrusted client, it must be wrapped with input and state validation.
+ * <li>The underlying driver is assumed to be correct. This implementation does not attempt to
+ * gracefully handle driver malfunction and such behavior will result in undefined behavior. If this
+ * service is to used with an untrusted driver, the driver must be wrapped with validation / error
+ * recovery code.
+ * <li>RemoteExceptions thrown by the driver are treated as RuntimeExceptions - they are not
+ * considered recoverable faults and should not occur in a properly functioning system.
+ * <li>There is no binder instance associated with this implementation. Do not call asBinder().
+ * <li>The implementation may throw a {@link RecoverableException} to indicate non-fatal,
+ * recoverable faults. The error code would one of the
+ * {@link android.media.soundtrigger_middleware.Status} constants. Any other exception
+ * thrown should be regarded as a bug in the implementation or one of its dependencies
+ * (assuming correct usage).
+ * <li>The implementation is designed for testability by featuring dependency injection (the
+ * underlying HAL driver instances are passed to the ctor) and by minimizing dependencies
+ * on Android runtime.
+ * <li>The implementation is thread-safe. This is achieved by a simplistic model, where all entry-
+ * points (both client API and driver callbacks) obtain a lock on the SoundTriggerModule instance
+ * for their entire scope. Any other method can be assumed to be running with the lock already
+ * obtained, so no further locking should be done. While this is not necessarily the most efficient
+ * synchronization strategy, it is very easy to reason about and this code is likely not on any
+ * performance-critical
+ * path.
+ * </ul>
+ *
+ * @hide
+ */
+class SoundTriggerModule {
+    static private final String TAG = "SoundTriggerModule";
+    @NonNull private final ISoundTriggerHw2 mHalService;
+    @NonNull private final SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider;
+    private final Set<Session> mActiveSessions = new HashSet<>();
+    private int mNumLoadedModels = 0;
+    private SoundTriggerModuleProperties mProperties = null;
+    private boolean mRecognitionAvailable;
+
+    /**
+     * Ctor.
+     *
+     * @param halService The underlying HAL driver.
+     */
+    SoundTriggerModule(@NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw halService,
+            @NonNull SoundTriggerMiddlewareImpl.AudioSessionProvider audioSessionProvider) {
+        assert halService != null;
+        mHalService = new SoundTriggerHw2Compat(halService);
+        mAudioSessionProvider = audioSessionProvider;
+        mProperties = ConversionUtil.hidl2aidlProperties(mHalService.getProperties());
+
+        // We conservatively assume that external capture is active until explicitly told otherwise.
+        mRecognitionAvailable = mProperties.concurrentCapture;
+    }
+
+    /**
+     * Establish a client session with this module.
+     *
+     * This module may be shared by multiple clients, each will get its own session. While resources
+     * are shared between the clients, each session has its own state and data should not be shared
+     * across sessions.
+     *
+     * @param callback The client callback, which will be used for all messages. This is a oneway
+     *                 callback, so will never block, throw an unchecked exception or return a
+     *                 value.
+     * @return The interface through which this module can be controlled.
+     */
+    synchronized @NonNull
+    Session attach(@NonNull ISoundTriggerCallback callback) {
+        Log.d(TAG, "attach()");
+        Session session = new Session(callback);
+        mActiveSessions.add(session);
+        return session;
+    }
+
+    /**
+     * Query the module's properties.
+     *
+     * @return The properties structure.
+     */
+    synchronized @NonNull
+    SoundTriggerModuleProperties getProperties() {
+        return mProperties;
+    }
+
+    /**
+     * Notify the module that external capture has started / finished, using the same input device
+     * used for recognition.
+     * If the underlying driver does not support recognition while capturing, capture will be
+     * aborted, and the recognition callback will receive and abort event. In addition, all active
+     * clients will be notified of the change in state.
+     *
+     * @param active true iff external capture is active.
+     */
+    synchronized void setExternalCaptureState(boolean active) {
+        Log.d(TAG, String.format("setExternalCaptureState(active=%b)", active));
+        if (mProperties.concurrentCapture) {
+            // If we support concurrent capture, we don't care about any of this.
+            return;
+        }
+        mRecognitionAvailable = !active;
+        if (!mRecognitionAvailable) {
+            // Our module does not support recognition while a capture is active -
+            // need to abort all active recognitions.
+            for (Session session : mActiveSessions) {
+                session.abortActiveRecognitions();
+            }
+        }
+        for (Session session : mActiveSessions) {
+            session.notifyRecognitionAvailability();
+        }
+    }
+
+    /**
+     * Remove session from the list of active sessions.
+     *
+     * @param session The session to remove.
+     */
+    private void removeSession(@NonNull Session session) {
+        mActiveSessions.remove(session);
+    }
+
+    /** State of a single sound model. */
+    private enum ModelState {
+        /** Initial state, until load() is called. */
+        INIT,
+        /** Model is loaded, but recognition is not active. */
+        LOADED,
+        /** Model is loaded and recognition is active. */
+        ACTIVE
+    }
+
+    /**
+     * A single client session with this module.
+     *
+     * This is the main interface used to interact with this module.
+     */
+    private class Session implements ISoundTriggerModule {
+        private ISoundTriggerCallback mCallback;
+        private Map<Integer, Model> mLoadedModels = new HashMap<>();
+
+        /**
+         * Ctor.
+         *
+         * @param callback The client callback interface.
+         */
+        private Session(@NonNull ISoundTriggerCallback callback) {
+            mCallback = callback;
+            notifyRecognitionAvailability();
+        }
+
+        @Override
+        public void detach() {
+            Log.d(TAG, "detach()");
+            synchronized (SoundTriggerModule.this) {
+                removeSession(this);
+            }
+        }
+
+        @Override
+        public int loadModel(@NonNull SoundModel model) {
+            Log.d(TAG, String.format("loadModel(model=%s)", model));
+            synchronized (SoundTriggerModule.this) {
+                if (mNumLoadedModels == mProperties.maxSoundModels) {
+                    throw new RecoverableException(Status.RESOURCE_CONTENTION,
+                            "Maximum number of models loaded.");
+                }
+                Model loadedModel = new Model();
+                int result = loadedModel.load(model);
+                ++mNumLoadedModels;
+                return result;
+            }
+        }
+
+        @Override
+        public int loadPhraseModel(@NonNull PhraseSoundModel model) {
+            Log.d(TAG, String.format("loadPhraseModel(model=%s)", model));
+            synchronized (SoundTriggerModule.this) {
+                if (mNumLoadedModels == mProperties.maxSoundModels) {
+                    throw new RecoverableException(Status.RESOURCE_CONTENTION,
+                            "Maximum number of models loaded.");
+                }
+                Model loadedModel = new Model();
+                int result = loadedModel.load(model);
+                ++mNumLoadedModels;
+                Log.d(TAG, String.format("loadPhraseModel()->%d", result));
+                return result;
+            }
+        }
+
+        @Override
+        public void unloadModel(int modelHandle) {
+            Log.d(TAG, String.format("unloadModel(handle=%d)", modelHandle));
+            synchronized (SoundTriggerModule.this) {
+                mLoadedModels.get(modelHandle).unload();
+                --mNumLoadedModels;
+            }
+        }
+
+        @Override
+        public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
+            Log.d(TAG,
+                    String.format("startRecognition(handle=%d, config=%s)", modelHandle, config));
+            synchronized (SoundTriggerModule.this) {
+                mLoadedModels.get(modelHandle).startRecognition(config);
+            }
+        }
+
+        @Override
+        public void stopRecognition(int modelHandle) {
+            Log.d(TAG, String.format("stopRecognition(handle=%d)", modelHandle));
+            synchronized (SoundTriggerModule.this) {
+                mLoadedModels.get(modelHandle).stopRecognition();
+            }
+        }
+
+        @Override
+        public void forceRecognitionEvent(int modelHandle) {
+            Log.d(TAG, String.format("forceRecognitionEvent(handle=%d)", modelHandle));
+            synchronized (SoundTriggerModule.this) {
+                mLoadedModels.get(modelHandle).forceRecognitionEvent();
+            }
+        }
+
+        @Override
+        public void setModelParameter(int modelHandle, int modelParam, int value)
+                throws RemoteException {
+            Log.d(TAG,
+                    String.format("setModelParameter(handle=%d, param=%d, value=%d)", modelHandle,
+                            modelParam, value));
+            synchronized (SoundTriggerModule.this) {
+                mLoadedModels.get(modelHandle).setParameter(modelParam, value);
+            }
+        }
+
+        @Override
+        public int getModelParameter(int modelHandle, int modelParam) throws RemoteException {
+            Log.d(TAG, String.format("getModelParameter(handle=%d, param=%d)", modelHandle,
+                    modelParam));
+            synchronized (SoundTriggerModule.this) {
+                return mLoadedModels.get(modelHandle).getParameter(modelParam);
+            }
+        }
+
+        @Override
+        @Nullable
+        public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam) {
+            Log.d(TAG, String.format("queryModelParameterSupport(handle=%d, param=%d)", modelHandle,
+                    modelParam));
+            synchronized (SoundTriggerModule.this) {
+                return mLoadedModels.get(modelHandle).queryModelParameterSupport(modelParam);
+            }
+        }
+
+        /**
+         * Abort all currently active recognitions.
+         */
+        private void abortActiveRecognitions() {
+            for (Model model : mLoadedModels.values()) {
+                model.abortActiveRecognition();
+            }
+        }
+
+        private void notifyRecognitionAvailability() {
+            try {
+                mCallback.onRecognitionAvailabilityChange(mRecognitionAvailable);
+            } catch (RemoteException e) {
+                // Dead client will be handled by binderDied() - no need to handle here.
+                // In any case, client callbacks are considered best effort.
+                Log.e(TAG, "Client callback execption.", e);
+            }
+        }
+
+        @Override
+        public @NonNull
+        IBinder asBinder() {
+            throw new UnsupportedOperationException(
+                    "This implementation is not intended to be used directly with Binder.");
+        }
+
+        /**
+         * A single sound model in the system.
+         *
+         * All model-based operations are delegated to this class and implemented here.
+         */
+        private class Model implements ISoundTriggerHw2.Callback {
+            public int mHandle;
+            private ModelState mState = ModelState.INIT;
+            private int mModelType = SoundModelType.UNKNOWN;
+            private SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession mSession;
+
+            private @NonNull
+            ModelState getState() {
+                return mState;
+            }
+
+            private void setState(@NonNull ModelState state) {
+                mState = state;
+                SoundTriggerModule.this.notifyAll();
+            }
+
+            private void waitStateChange() throws InterruptedException {
+                SoundTriggerModule.this.wait();
+            }
+
+            private int load(@NonNull SoundModel model) {
+                mModelType = model.type;
+                ISoundTriggerHw.SoundModel hidlModel = ConversionUtil.aidl2hidlSoundModel(model);
+
+                mSession = mAudioSessionProvider.acquireSession();
+                try {
+                    mHandle = mHalService.loadSoundModel(hidlModel, this, 0);
+                } catch (Exception e) {
+                    mAudioSessionProvider.releaseSession(mSession.mSessionHandle);
+                    throw e;
+                }
+
+                setState(ModelState.LOADED);
+                mLoadedModels.put(mHandle, this);
+                return mHandle;
+            }
+
+            private int load(@NonNull PhraseSoundModel model) {
+                mModelType = model.common.type;
+                ISoundTriggerHw.PhraseSoundModel hidlModel =
+                        ConversionUtil.aidl2hidlPhraseSoundModel(model);
+
+                mSession = mAudioSessionProvider.acquireSession();
+                try {
+                    mHandle = mHalService.loadPhraseSoundModel(hidlModel, this, 0);
+                } catch (Exception e) {
+                    mAudioSessionProvider.releaseSession(mSession.mSessionHandle);
+                    throw e;
+                }
+
+                setState(ModelState.LOADED);
+                mLoadedModels.put(mHandle, this);
+                return mHandle;
+            }
+
+            private void unload() {
+                mAudioSessionProvider.releaseSession(mSession.mSessionHandle);
+                mHalService.unloadSoundModel(mHandle);
+                mLoadedModels.remove(mHandle);
+            }
+
+            private void startRecognition(@NonNull RecognitionConfig config) {
+                if (!mRecognitionAvailable) {
+                    // Recognition is unavailable - send an abort event immediately.
+                    notifyAbort();
+                    return;
+                }
+                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig hidlConfig =
+                        ConversionUtil.aidl2hidlRecognitionConfig(config);
+                hidlConfig.header.captureDevice = mSession.mDeviceHandle;
+                hidlConfig.header.captureHandle = mSession.mIoHandle;
+                mHalService.startRecognition(mHandle, hidlConfig, this, 0);
+                setState(ModelState.ACTIVE);
+            }
+
+            private void stopRecognition() {
+                if (getState() == ModelState.LOADED) {
+                    // This call is idempotent in order to avoid races.
+                    return;
+                }
+                mHalService.stopRecognition(mHandle);
+                setState(ModelState.LOADED);
+            }
+
+            /** Request a forced recognition event. Will do nothing if recognition is inactive. */
+            private void forceRecognitionEvent() {
+                if (getState() != ModelState.ACTIVE) {
+                    // This call is idempotent in order to avoid races.
+                    return;
+                }
+                mHalService.getModelState(mHandle);
+            }
+
+
+            private void setParameter(int modelParam, int value) {
+                mHalService.setModelParameter(mHandle,
+                        ConversionUtil.aidl2hidlModelParameter(modelParam), value);
+            }
+
+            private int getParameter(int modelParam) {
+                return mHalService.getModelParameter(mHandle,
+                        ConversionUtil.aidl2hidlModelParameter(modelParam));
+            }
+
+            @Nullable
+            private ModelParameterRange queryModelParameterSupport(int modelParam) {
+                return ConversionUtil.hidl2aidlModelParameterRange(
+                        mHalService.queryParameter(mHandle,
+                                ConversionUtil.aidl2hidlModelParameter(modelParam)));
+            }
+
+            /** Abort the recognition, if active. */
+            private void abortActiveRecognition() {
+                // If we're inactive, do nothing.
+                if (getState() != ModelState.ACTIVE) {
+                    return;
+                }
+                // Stop recognition.
+                stopRecognition();
+
+                // Notify the client that recognition has been aborted.
+                notifyAbort();
+            }
+
+            /** Notify the client that recognition has been aborted. */
+            private void notifyAbort() {
+                try {
+                    switch (mModelType) {
+                        case SoundModelType.GENERIC: {
+                            android.media.soundtrigger_middleware.RecognitionEvent event =
+                                    new android.media.soundtrigger_middleware.RecognitionEvent();
+                            event.status =
+                                    android.media.soundtrigger_middleware.RecognitionStatus.ABORTED;
+                            mCallback.onRecognition(mHandle, event);
+                        }
+                        break;
+
+                        case SoundModelType.KEYPHRASE: {
+                            android.media.soundtrigger_middleware.PhraseRecognitionEvent event =
+                                    new android.media.soundtrigger_middleware.PhraseRecognitionEvent();
+                            event.common =
+                                    new android.media.soundtrigger_middleware.RecognitionEvent();
+                            event.common.status =
+                                    android.media.soundtrigger_middleware.RecognitionStatus.ABORTED;
+                            mCallback.onPhraseRecognition(mHandle, event);
+                        }
+                        break;
+
+                        default:
+                            Log.e(TAG, "Unknown model type: " + mModelType);
+
+                    }
+                } catch (RemoteException e) {
+                    // Dead client will be handled by binderDied() - no need to handle here.
+                    // In any case, client callbacks are considered best effort.
+                    Log.e(TAG, "Client callback execption.", e);
+                }
+            }
+
+            @Override
+            public void recognitionCallback(
+                    @NonNull ISoundTriggerHwCallback.RecognitionEvent recognitionEvent,
+                    int cookie) {
+                Log.d(TAG, String.format("recognitionCallback_2_1(event=%s, cookie=%d)",
+                        recognitionEvent, cookie));
+                synchronized (SoundTriggerModule.this) {
+                    android.media.soundtrigger_middleware.RecognitionEvent aidlEvent =
+                            ConversionUtil.hidl2aidlRecognitionEvent(recognitionEvent);
+                    aidlEvent.captureSession = mSession.mSessionHandle;
+                    try {
+                        mCallback.onRecognition(mHandle, aidlEvent);
+                    } catch (RemoteException e) {
+                        // Dead client will be handled by binderDied() - no need to handle here.
+                        // In any case, client callbacks are considered best effort.
+                        Log.e(TAG, "Client callback execption.", e);
+                    }
+                    if (aidlEvent.status
+                            != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
+                        setState(ModelState.LOADED);
+                    }
+                }
+            }
+
+            @Override
+            public void phraseRecognitionCallback(
+                    @NonNull ISoundTriggerHwCallback.PhraseRecognitionEvent phraseRecognitionEvent,
+                    int cookie) {
+                Log.d(TAG, String.format("phraseRecognitionCallback_2_1(event=%s, cookie=%d)",
+                        phraseRecognitionEvent, cookie));
+                synchronized (SoundTriggerModule.this) {
+                    android.media.soundtrigger_middleware.PhraseRecognitionEvent aidlEvent =
+                            ConversionUtil.hidl2aidlPhraseRecognitionEvent(phraseRecognitionEvent);
+                    aidlEvent.common.captureSession = mSession.mSessionHandle;
+                    try {
+                        mCallback.onPhraseRecognition(mHandle, aidlEvent);
+                    } catch (RemoteException e) {
+                        // Dead client will be handled by binderDied() - no need to handle here.
+                        // In any case, client callbacks are considered best effort.
+                        Log.e(TAG, "Client callback execption.", e);
+                    }
+                    if (aidlEvent.common.status
+                            != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
+                        setState(ModelState.LOADED);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/TEST_MAPPING b/services/core/java/com/android/server/soundtrigger_middleware/TEST_MAPPING
new file mode 100644
index 0000000..9ed894b
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.soundtrigger_middleware"
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/UuidUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/UuidUtil.java
new file mode 100644
index 0000000..80f69d0
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/UuidUtil.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import java.util.regex.Pattern;
+
+/**
+ * Utilities for representing UUIDs as strings.
+ *
+ * @hide
+ */
+public class UuidUtil {
+    /**
+     * Regex pattern that can be used to validate / extract the various fields of a string-formatted
+     * UUID.
+     */
+    static final Pattern PATTERN = Pattern.compile("^([a-fA-F0-9]{8})-" +
+            "([a-fA-F0-9]{4})-" +
+            "([a-fA-F0-9]{4})-" +
+            "([a-fA-F0-9]{4})-" +
+            "([a-fA-F0-9]{2})" +
+            "([a-fA-F0-9]{2})" +
+            "([a-fA-F0-9]{2})" +
+            "([a-fA-F0-9]{2})" +
+            "([a-fA-F0-9]{2})" +
+            "([a-fA-F0-9]{2})$");
+
+    /** Printf-style pattern for formatting the various fields of a UUID as a string. */
+    static final String FORMAT = "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x";
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
new file mode 100644
index 0000000..4898e6b
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
@@ -0,0 +1,117 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.Nullable;
+import android.media.soundtrigger_middleware.ConfidenceLevel;
+import android.media.soundtrigger_middleware.ModelParameter;
+import android.media.soundtrigger_middleware.Phrase;
+import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
+import android.media.soundtrigger_middleware.PhraseSoundModel;
+import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger_middleware.RecognitionMode;
+import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger_middleware.SoundModelType;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Utilities for asserting the validity of various data types used by this module.
+ * Each of the methods below would throw an {@link IllegalArgumentException} if its input is
+ * invalid. The input's validity is determined irrespective of any context. In cases where the valid
+ * value space is further limited by state, it is the caller's responsibility to assert.
+ *
+ * @hide
+ */
+public class ValidationUtil {
+    static void validateUuid(@Nullable String uuid) {
+        Preconditions.checkNotNull(uuid);
+        Matcher matcher = UuidUtil.PATTERN.matcher(uuid);
+        if (!matcher.matches()) {
+            throw new IllegalArgumentException(
+                    "Illegal format for UUID: " + uuid);
+        }
+    }
+
+    static void validateGenericModel(@Nullable SoundModel model) {
+        validateModel(model, SoundModelType.GENERIC);
+    }
+
+    static void validateModel(@Nullable SoundModel model, int expectedType) {
+        Preconditions.checkNotNull(model);
+        if (model.type != expectedType) {
+            throw new IllegalArgumentException("Invalid type");
+        }
+        validateUuid(model.uuid);
+        validateUuid(model.vendorUuid);
+        Preconditions.checkNotNull(model.data);
+    }
+
+    static void validatePhraseModel(@Nullable PhraseSoundModel model) {
+        Preconditions.checkNotNull(model);
+        validateModel(model.common, SoundModelType.KEYPHRASE);
+        Preconditions.checkNotNull(model.phrases);
+        for (Phrase phrase : model.phrases) {
+            Preconditions.checkNotNull(phrase);
+            if ((phrase.recognitionModes & ~(RecognitionMode.VOICE_TRIGGER
+                    | RecognitionMode.USER_IDENTIFICATION | RecognitionMode.USER_AUTHENTICATION
+                    | RecognitionMode.GENERIC_TRIGGER)) != 0) {
+                throw new IllegalArgumentException("Invalid recognitionModes");
+            }
+            Preconditions.checkNotNull(phrase.users);
+            Preconditions.checkNotNull(phrase.locale);
+            Preconditions.checkNotNull(phrase.text);
+        }
+    }
+
+    static void validateRecognitionConfig(@Nullable RecognitionConfig config) {
+        Preconditions.checkNotNull(config);
+        Preconditions.checkNotNull(config.phraseRecognitionExtras);
+        for (PhraseRecognitionExtra extra : config.phraseRecognitionExtras) {
+            Preconditions.checkNotNull(extra);
+            if ((extra.recognitionModes & ~(RecognitionMode.VOICE_TRIGGER
+                    | RecognitionMode.USER_IDENTIFICATION | RecognitionMode.USER_AUTHENTICATION
+                    | RecognitionMode.GENERIC_TRIGGER)) != 0) {
+                throw new IllegalArgumentException("Invalid recognitionModes");
+            }
+            if (extra.confidenceLevel < 0 || extra.confidenceLevel > 100) {
+                throw new IllegalArgumentException("Invalid confidenceLevel");
+            }
+            Preconditions.checkNotNull(extra.levels);
+            for (ConfidenceLevel level : extra.levels) {
+                Preconditions.checkNotNull(level);
+                if (level.levelPercent < 0 || level.levelPercent > 100) {
+                    throw new IllegalArgumentException("Invalid confidenceLevel");
+                }
+            }
+        }
+        Preconditions.checkNotNull(config.data);
+    }
+
+    static void validateModelParameter(int modelParam) {
+        switch (modelParam) {
+            case ModelParameter.THRESHOLD_FACTOR:
+                return;
+
+            default:
+                throw new IllegalArgumentException("Invalid model parameter");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 677d2a1..6f124ac 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -3594,7 +3594,10 @@
             pw.println(prefix + "* " + task);
             task.dump(pw, prefix + "  ");
             final ArrayList<ActivityRecord> activities = new ArrayList<>();
-            forAllActivities((Consumer<ActivityRecord>) activities::add);
+            // Add activities by traversing the hierarchy from bottom to top, since activities
+            // are dumped in reverse order in {@link ActivityStackSupervisor#dumpHistoryList()}.
+            forAllActivities((Consumer<ActivityRecord>) activities::add,
+                    false /* traverseTopToBottom */);
             dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
                     dumpPackage, false, null, task);
         });
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 23083c9..674955e 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -98,6 +98,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.AuxiliaryResolveInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
@@ -1309,9 +1310,11 @@
             String resolvedType, int userId) {
         if (auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo) {
             // request phase two resolution
-            mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo(
+            PackageManagerInternal packageManager = mService.getPackageManagerInternalLocked();
+            boolean isRequesterInstantApp = packageManager.isInstantApp(callingPackage, userId);
+            packageManager.requestInstantAppResolutionPhaseTwo(
                     auxiliaryResponse, originalIntent, resolvedType, callingPackage,
-                    verificationBundle, userId);
+                    isRequesterInstantApp, verificationBundle, userId);
         }
         return InstantAppResolver.buildEphemeralInstallerIntent(
                 originalIntent,
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index fd8094c..a34b7fd 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -35,6 +35,7 @@
         "com_android_server_power_PowerManagerService.cpp",
         "com_android_server_security_VerityUtils.cpp",
         "com_android_server_SerialService.cpp",
+        "com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp",
         "com_android_server_storage_AppFuseBridge.cpp",
         "com_android_server_SystemServer.cpp",
         "com_android_server_TestNetworkService.cpp",
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index 906b568..be11b86 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -24,6 +24,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/fsverity.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
@@ -33,40 +34,6 @@
 
 #include <android-base/unique_fd.h>
 
-// TODO(112037636): Always include once fsverity.h is upstreamed.
-#if __has_include(<linux/fsverity.h>)
-#include <linux/fsverity.h>
-#else
-
-#include <linux/limits.h>
-#include <linux/ioctl.h>
-#include <linux/types.h>
-
-#define FS_VERITY_HASH_ALG_SHA256	1
-
-struct fsverity_enable_arg {
-	__u32 version;
-	__u32 hash_algorithm;
-	__u32 block_size;
-	__u32 salt_size;
-	__u64 salt_ptr;
-	__u32 sig_size;
-	__u32 __reserved1;
-	__u64 sig_ptr;
-	__u64 __reserved2[11];
-};
-
-struct fsverity_digest {
-    __u16 digest_algorithm;
-    __u16 digest_size; /* input/output */
-    __u8 digest[];
-};
-
-#define FS_IOC_ENABLE_VERITY	_IOW('f', 133, struct fsverity_enable_arg)
-#define FS_IOC_MEASURE_VERITY	_IOWR('f', 134, struct fsverity_digest)
-
-#endif
-
 const int kSha256Bytes = 32;
 
 namespace android {
diff --git a/services/core/jni/com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp b/services/core/jni/com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp
new file mode 100644
index 0000000..774534f
--- /dev/null
+++ b/services/core/jni/com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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 <sstream>
+
+#include "core_jni_helpers.h"
+#include <media/AudioSystem.h>
+
+namespace android {
+
+namespace {
+
+#define PACKAGE "com/android/server/soundtrigger_middleware"
+#define CLASSNAME PACKAGE "/AudioSessionProviderImpl"
+#define SESSION_CLASSNAME PACKAGE "/SoundTriggerMiddlewareImpl$AudioSessionProvider$AudioSession"
+
+jobject acquireAudioSession(
+        JNIEnv* env,
+        jobject clazz) {
+
+    audio_session_t session;
+    audio_io_handle_t ioHandle;
+    audio_devices_t device;
+
+    status_t status = AudioSystem::acquireSoundTriggerSession(&session,
+                                                              &ioHandle,
+                                                              &device);
+    if (status != 0) {
+        std::ostringstream message;
+        message
+                << "AudioSystem::acquireSoundTriggerSession returned an error code: "
+                << status;
+        env->ThrowNew(FindClassOrDie(env, "java/lang/RuntimeException"),
+                      message.str().c_str());
+        return nullptr;
+    }
+
+    jclass cls = FindClassOrDie(env, SESSION_CLASSNAME);
+    jmethodID ctor = GetMethodIDOrDie(env, cls, "<init>", "(III)V");
+    return env->NewObject(cls,
+                          ctor,
+                          static_cast<int>(session),
+                          static_cast<int>(ioHandle),
+                          static_cast<int>(device));
+}
+
+void releaseAudioSession(JNIEnv* env, jobject clazz, jint handle) {
+    status_t status =
+            AudioSystem::releaseSoundTriggerSession(static_cast<audio_session_t>(handle));
+
+    if (status != 0) {
+        std::ostringstream message;
+        message
+                << "AudioSystem::releaseAudioSystemSession returned an error code: "
+                << status;
+        env->ThrowNew(FindClassOrDie(env, "java/lang/RuntimeException"),
+                      message.str().c_str());
+    }
+}
+
+const JNINativeMethod g_methods[] = {
+        {"acquireSession", "()L" SESSION_CLASSNAME ";",
+         reinterpret_cast<void*>(acquireAudioSession)},
+        {"releaseSession", "(I)V",
+         reinterpret_cast<void*>(releaseAudioSession)},
+};
+
+}  // namespace
+
+int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
+        JNIEnv* env) {
+    return RegisterMethodsOrDie(env,
+                                CLASSNAME,
+                                g_methods,
+                                NELEM(g_methods));
+}
+
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 692c9d2..165edf1 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -56,6 +56,8 @@
 int register_android_server_security_VerityUtils(JNIEnv* env);
 int register_android_server_am_AppCompactor(JNIEnv* env);
 int register_android_server_am_LowMemDetector(JNIEnv* env);
+int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
+        JNIEnv* env);
 };
 
 using namespace android;
@@ -105,5 +107,7 @@
     register_android_server_security_VerityUtils(env);
     register_android_server_am_AppCompactor(env);
     register_android_server_am_LowMemDetector(env);
+    register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
+            env);
     return JNI_VERSION_1_4;
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 078bab4..d91ec42 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2447,7 +2447,7 @@
                 Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId) != 0) {
             profileOwner.ensureUserRestrictions().putBoolean(
                     UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true);
-            saveUserRestrictionsLocked(userId);
+            saveUserRestrictionsLocked(userId, /* parent = */ false);
             mInjector.settingsSecurePutIntForUser(
                     Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED, 0, userId);
         }
@@ -2478,7 +2478,7 @@
             }
             admin.defaultEnabledRestrictionsAlreadySet.addAll(restrictionsToSet);
             Slog.i(LOG_TAG, "Enabled the following restrictions by default: " + restrictionsToSet);
-            saveUserRestrictionsLocked(userId);
+            saveUserRestrictionsLocked(userId, /* parent = */ false);
         }
     }
 
@@ -8039,7 +8039,7 @@
                 activeAdmin.defaultEnabledRestrictionsAlreadySet.addAll(restrictions);
                 Slog.i(LOG_TAG, "Enabled the following restrictions by default: " + restrictions);
 
-                saveUserRestrictionsLocked(userId);
+                saveUserRestrictionsLocked(userId, /* parent = */ false);
             }
 
             long ident = mInjector.binderClearCallingIdentity();
@@ -10310,24 +10310,33 @@
     }
 
     @Override
-    public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner) {
+    public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner,
+            boolean parent) {
         Preconditions.checkNotNull(who, "ComponentName is null");
         if (!UserRestrictionsUtils.isValidRestriction(key)) {
             return;
         }
 
-        final int userHandle = mInjector.userHandleGetCallingUserId();
+        int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             final ActiveAdmin activeAdmin =
                     getActiveAdminForCallerLocked(who,
-                            DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+                            DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
             final boolean isDeviceOwner = isDeviceOwner(who, userHandle);
+
             if (isDeviceOwner) {
                 if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) {
                     throw new SecurityException("Device owner cannot set user restriction " + key);
                 }
-            } else { // profile owner
-                if (!UserRestrictionsUtils.canProfileOwnerChange(key, userHandle)) {
+                if (parent) {
+                    throw new IllegalArgumentException(
+                            "Cannot use the parent instance in Device Owner mode");
+                }
+            } else {
+                if (!(UserRestrictionsUtils.canProfileOwnerChange(key, userHandle) || (
+                        isProfileOwnerOfOrganizationOwnedDevice(activeAdmin) && parent
+                        && UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange(
+                                key)))) {
                     throw new SecurityException("Profile owner cannot set user restriction " + key);
                 }
             }
@@ -10339,7 +10348,7 @@
             } else {
                 restrictions.remove(key);
             }
-            saveUserRestrictionsLocked(userHandle);
+            saveUserRestrictionsLocked(userHandle, parent);
         }
         final int eventId = enabledFromThisOwner
                 ? DevicePolicyEnums.ADD_USER_RESTRICTION
@@ -10357,9 +10366,9 @@
         }
     }
 
-    private void saveUserRestrictionsLocked(int userId) {
+    private void saveUserRestrictionsLocked(int userId, boolean parent) {
         saveSettingsLocked(userId);
-        pushUserRestrictions(userId);
+        pushUserRestrictions(parent ? getProfileParentId(userId) : userId);
         sendChangedNotification(userId);
     }
 
@@ -10368,6 +10377,7 @@
             final boolean isDeviceOwner = mOwners.isDeviceOwnerUserId(userId);
             Bundle userRestrictions = null;
             final int restrictionOwnerType;
+            final int originatingUserId;
 
             if (isDeviceOwner) {
                 final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
@@ -10377,6 +10387,7 @@
                 userRestrictions = deviceOwner.userRestrictions;
                 addOrRemoveDisableCameraRestriction(userRestrictions, deviceOwner);
                 restrictionOwnerType = UserManagerInternal.OWNER_TYPE_DEVICE_OWNER;
+                originatingUserId = deviceOwner.getUserHandle().getIdentifier();
             } else {
                 final ActiveAdmin profileOwnerOfOrganizationOwnedDevice =
                         getProfileOwnerOfOrganizationOwnedDeviceLocked(userId);
@@ -10391,21 +10402,25 @@
                     userRestrictions = parent.userRestrictions;
                     userRestrictions = addOrRemoveDisableCameraRestriction(userRestrictions,
                             parent);
+                    originatingUserId =
+                            profileOwnerOfOrganizationOwnedDevice.getUserHandle().getIdentifier();
                 } else {
                     final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
 
                     if (profileOwner != null) {
                         userRestrictions = profileOwner.userRestrictions;
                         restrictionOwnerType = UserManagerInternal.OWNER_TYPE_PROFILE_OWNER;
+                        originatingUserId = profileOwner.getUserHandle().getIdentifier();
                     } else {
                         restrictionOwnerType = UserManagerInternal.OWNER_TYPE_NO_OWNER;
+                        originatingUserId = userId;
                     }
                     userRestrictions = addOrRemoveDisableCameraRestriction(
                             userRestrictions, userId);
                 }
             }
-            mUserManagerInternal.setDevicePolicyUserRestrictions(userId, userRestrictions,
-                    restrictionOwnerType);
+            mUserManagerInternal.setDevicePolicyUserRestrictions(originatingUserId,
+                    userRestrictions, restrictionOwnerType);
         }
     }
 
@@ -10435,14 +10450,18 @@
     }
 
     @Override
-    public Bundle getUserRestrictions(ComponentName who) {
+    public Bundle getUserRestrictions(ComponentName who, boolean parent) {
         if (!mHasFeature) {
             return null;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
+
         synchronized (getLockObject()) {
             final ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(who,
-                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+            if (parent) {
+                enforceProfileOwnerOfOrganizationOwnedDevice(activeAdmin);
+            }
             return activeAdmin.userRestrictions;
         }
     }
@@ -11241,7 +11260,7 @@
                 } else {
                     try {
                         setUserRestriction(who, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
-                                (Integer.parseInt(value) == 0) ? true : false);
+                                (Integer.parseInt(value) == 0) ? true : false, /* parent */ false);
                         DevicePolicyEventLogger
                                 .createEvent(DevicePolicyEnums.SET_SECURE_SETTING)
                                 .setAdmin(who)
@@ -11286,7 +11305,7 @@
         Preconditions.checkNotNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            setUserRestriction(who, UserManager.DISALLOW_UNMUTE_DEVICE, on);
+            setUserRestriction(who, UserManager.DISALLOW_UNMUTE_DEVICE, on, /* parent */ false);
             DevicePolicyEventLogger
                     .createEvent(DevicePolicyEnums.SET_MASTER_VOLUME_MUTED)
                     .setAdmin(who)
@@ -13773,8 +13792,9 @@
         } else {
             deviceOwner.lastNetworkLoggingNotificationTimeMs = now;
         }
+        final PackageManagerInternal pm = mInjector.getPackageManagerInternal();
         final Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
-        intent.setPackage("com.android.systemui");
+        intent.setPackage(pm.getSystemUiServiceComponent().getPackageName());
         final PendingIntent pendingIntent = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0,
                 UserHandle.CURRENT);
         Notification notification =
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 401a094..7d6b0c9 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -30,12 +30,12 @@
 import android.app.AppCompatCallbacks;
 import android.app.INotificationManager;
 import android.app.usage.UsageStatsManagerInternal;
-import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.res.Configuration;
 import android.content.res.Resources.Theme;
 import android.database.sqlite.SQLiteCompatibilityWalFlags;
@@ -148,6 +148,7 @@
 import com.android.server.security.KeyChainSystemService;
 import com.android.server.signedconfig.SignedConfigService;
 import com.android.server.soundtrigger.SoundTriggerService;
+import com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareService;
 import com.android.server.statusbar.StatusBarManagerService;
 import com.android.server.storage.DeviceStorageMonitorService;
 import com.android.server.telecom.TelecomLoaderService;
@@ -1555,6 +1556,10 @@
             }
             t.traceEnd();
 
+            t.traceBegin("StartSoundTriggerMiddlewareService");
+            mSystemServiceManager.startService(SoundTriggerMiddlewareService.Lifecycle.class);
+            t.traceEnd();
+
             if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BROADCAST_RADIO)) {
                 t.traceBegin("StartBroadcastRadioService");
                 mSystemServiceManager.startService(BroadcastRadioService.class);
@@ -2395,9 +2400,9 @@
     }
 
     private static void startSystemUi(Context context, WindowManagerService windowManager) {
+        PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
         Intent intent = new Intent();
-        intent.setComponent(new ComponentName("com.android.systemui",
-                "com.android.systemui.SystemUIService"));
+        intent.setComponent(pm.getSystemUiServiceComponent());
         intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
         //Slog.d(TAG, "Starting service: " + intent);
         context.startServiceAsUser(intent, UserHandle.SYSTEM);
diff --git a/services/net/java/android/net/TcpKeepalivePacketData.java b/services/net/java/android/net/TcpKeepalivePacketData.java
index 7f2f499..aad75ae 100644
--- a/services/net/java/android/net/TcpKeepalivePacketData.java
+++ b/services/net/java/android/net/TcpKeepalivePacketData.java
@@ -19,7 +19,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.net.SocketKeepalive.InvalidPacketException;
 import android.net.util.IpUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index cf7919b..3910993 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -40,7 +40,6 @@
         "platformprotosnano",
         "hamcrest-library",
         "servicestests-utils",
-        "xml-writer-device-lib",
         "service-appsearch",
         "service-jobscheduler",
     ],
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/GestureManifoldTest.java
similarity index 83%
rename from services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java
rename to services/tests/servicestests/src/com/android/server/accessibility/gestures/GestureManifoldTest.java
index b707912..538e2d5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/GestureManifoldTest.java
@@ -24,22 +24,21 @@
 
 import android.accessibilityservice.AccessibilityService;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
-import android.util.DisplayMetrics;
-import android.view.GestureDetector;
 import android.view.MotionEvent;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
 
 /**
- * Tests for AccessibilityGestureDetector
+ * Tests for GestureManifold
  */
-public class AccessibilityGestureDetectorTest {
+public class GestureManifoldTest {
 
     // Constants for testRecognizeGesturePath()
     private static final PointF PATH_START = new PointF(300f, 300f);
@@ -47,24 +46,21 @@
     private static final long PATH_STEP_MILLISEC = 100;
 
     // Data used by all tests
-    private AccessibilityGestureDetector mDetector;
-    private AccessibilityGestureDetector.Listener mResultListener;
+    private GestureManifold mManifold;
+    private TouchState mState;
+    private GestureManifold.Listener mResultListener;
 
     @Before
     public void setUp() {
-        // Construct a mock Context.
-        DisplayMetrics displayMetricsMock = mock(DisplayMetrics.class);
-        displayMetricsMock.xdpi = 500;
-        displayMetricsMock.ydpi = 500;
-        Resources mockResources = mock(Resources.class);
-        when(mockResources.getDisplayMetrics()).thenReturn(displayMetricsMock);
-        Context contextMock = mock(Context.class);
-        when(contextMock.getResources()).thenReturn(mockResources);
+        Context context = InstrumentationRegistry.getContext();
+        // Construct a testable GestureManifold.
+        mResultListener = mock(GestureManifold.Listener.class);
+        mState = new TouchState();
+        mManifold = new GestureManifold(context, mResultListener, mState);
+        // Play the role of touch explorer in updating the shared state.
+        when(mResultListener.onGestureStarted()).thenReturn(onGestureStarted());
 
-        // Construct a testable AccessibilityGestureDetector.
-        mResultListener = mock(AccessibilityGestureDetector.Listener.class);
-        GestureDetector doubleTapDetectorMock = mock(GestureDetector.class);
-        mDetector = new AccessibilityGestureDetector(contextMock, mResultListener, doubleTapDetectorMock);
+
     }
 
 
@@ -141,8 +137,8 @@
         // For each path step from start (non-inclusive) to end ... add a motion point.
         for (int step = 1; step < numSteps; ++step) {
             path.add(new PointF(
-                (start.x + (stepX * (float) step)),
-                (start.y + (stepY * (float) step))));
+                    (start.x + (stepX * (float) step)),
+                    (start.y + (stepY * (float) step))));
         }
     }
 
@@ -170,12 +166,22 @@
                     point.x, point.y, 0);
 
             // Send event.
-            mDetector.onMotionEvent(event, event, policyFlags);
+            mState.onReceivedMotionEvent(event);
+            mManifold.onMotionEvent(event, event, policyFlags);
             eventTimeMs += PATH_STEP_MILLISEC;
+            if (mState.isClear()) {
+                mState.startTouchInteracting();
+            }
         }
 
+        mState.clear();
         // Check that correct gesture was recognized.
         verify(mResultListener).onGestureCompleted(
                 argThat(gestureEvent -> gestureEvent.getGestureId() == gestureId));
     }
+
+    private boolean onGestureStarted() {
+        mState.startGestureDetecting();
+        return false;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 4b1ec6f..a4ceadb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -74,7 +74,7 @@
     private TouchExplorer mTouchExplorer;
     private long mLastDownTime = Integer.MIN_VALUE;
 
-    // mock package-private AccessibilityGestureDetector class
+    // mock package-private GestureManifold class
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
@@ -108,7 +108,7 @@
     public void setUp() {
         Context context = InstrumentationRegistry.getContext();
         AccessibilityManagerService ams = new AccessibilityManagerService(context);
-        AccessibilityGestureDetector detector = mock(AccessibilityGestureDetector.class);
+        GestureManifold detector = mock(GestureManifold.class);
         mCaptor = new EventCaptor();
         mTouchExplorer = new TouchExplorer(context, ams, detector);
         mTouchExplorer.setNext(mCaptor);
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index a47a567..e90cb46 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -16,7 +16,11 @@
 
 package com.android.server.attention;
 
+import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
+
 import static com.android.server.attention.AttentionManagerService.ATTENTION_CACHE_BUFFER_SIZE;
+import static com.android.server.attention.AttentionManagerService.DEFAULT_STALE_AFTER_MILLIS;
+import static com.android.server.attention.AttentionManagerService.KEY_STALE_AFTER_MILLIS;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -35,6 +39,7 @@
 import android.os.IPowerManager;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.provider.DeviceConfig;
 import android.service.attention.IAttentionCallback;
 import android.service.attention.IAttentionService;
 
@@ -180,6 +185,45 @@
         assertThat(buffer.get(0)).isEqualTo(cache);
     }
 
+    @Test
+    public void testGetStaleAfterMillis_handlesGoodFlagValue() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_STALE_AFTER_MILLIS, "123", false);
+        assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(123);
+    }
+
+    @Test
+    public void testGetStaleAfterMillis_handlesBadFlagValue_1() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_STALE_AFTER_MILLIS, "-123", false);
+        assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
+                DEFAULT_STALE_AFTER_MILLIS);
+    }
+
+    @Test
+    public void testGetStaleAfterMillis_handlesBadFlagValue_2() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_STALE_AFTER_MILLIS, "15000", false);
+        assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
+                DEFAULT_STALE_AFTER_MILLIS);
+    }
+
+    @Test
+    public void testGetStaleAfterMillis_handlesBadFlagValue_3() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_STALE_AFTER_MILLIS, "abracadabra", false);
+        assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
+                DEFAULT_STALE_AFTER_MILLIS);
+    }
+
+    @Test
+    public void testGetStaleAfterMillis_handlesBadFlagValue_4() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_STALE_AFTER_MILLIS, "15_000L", false);
+        assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
+                DEFAULT_STALE_AFTER_MILLIS);
+    }
+
     private class MockIAttentionService implements IAttentionService {
         public void checkAttention(IAttentionCallback callback) throws RemoteException {
             callback.onSuccess(0, 0);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/OWNERS b/services/tests/servicestests/src/com/android/server/biometrics/OWNERS
new file mode 100644
index 0000000..8765c9a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+kchyn@google.com
+jaggies@google.com
+curtislb@google.com
+ilyamaty@google.com
+joshmccloskey@google.com
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 7267976..cb99c11 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -22,15 +22,13 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.compat.annotation.Change;
-import com.android.compat.annotation.XmlWriter;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.util.UUID;
 
 @RunWith(AndroidJUnit4.class)
@@ -50,18 +48,10 @@
         return dir;
     }
 
-    private void writeChangesToFile(Change[] changes, File f) {
-        XmlWriter writer = new XmlWriter();
-        for (Change change: changes) {
-            writer.addChange(change);
-        }
-        try {
-            f.createNewFile();
-            writer.write(new FileOutputStream(f));
-        } catch (IOException e) {
-            throw new RuntimeException(
-                    "Encountered an error while writing compat config file", e);
-        }
+    private void writeToFile(File dir, String filename, String content) throws IOException {
+        OutputStream os = new FileOutputStream(new File(dir, filename));
+        os.write(content.getBytes());
+        os.close();
     }
 
     @Test
@@ -173,13 +163,15 @@
     }
 
     @Test
-    public void testReadConfig() {
-        Change[] changes = {new Change(1234L, "MY_CHANGE1", false, 2, null), new Change(1235L,
-                "MY_CHANGE2", true, null, "description"), new Change(1236L, "MY_CHANGE3", false,
-                null, "")};
+    public void testReadConfig() throws IOException {
+        String configXml = "<config>"
+                + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />"
+                + "<compat-change id=\"1235\" name=\"MY_CHANGE2\" disabled=\"true\" />"
+                + "<compat-change id=\"1236\" name=\"MY_CHANGE3\" />"
+                + "</config>";
 
         File dir = createTempDir();
-        writeChangesToFile(changes, new File(dir.getPath() + "/platform_compat_config.xml"));
+        writeToFile(dir, "platform_compat_config.xml", configXml);
 
         CompatConfig pc = new CompatConfig();
         pc.initConfigFromLib(dir);
@@ -191,17 +183,18 @@
     }
 
     @Test
-    public void testReadConfigMultipleFiles() {
-        Change[] changes1 = {new Change(1234L, "MY_CHANGE1", false, 2, null)};
-        Change[] changes2 = {new Change(1235L, "MY_CHANGE2", true, null, ""), new Change(1236L,
-                "MY_CHANGE3", false, null, null)};
+    public void testReadConfigMultipleFiles() throws IOException {
+        String configXml1 = "<config>"
+                + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />"
+                + "</config>";
+        String configXml2 = "<config>"
+                + "<compat-change id=\"1235\" name=\"MY_CHANGE2\" disabled=\"true\" />"
+                + "<compat-change id=\"1236\" name=\"MY_CHANGE3\" />"
+                + "</config>";
 
         File dir = createTempDir();
-        writeChangesToFile(changes1,
-                new File(dir.getPath() + "/libcore_platform_compat_config.xml"));
-        writeChangesToFile(changes2,
-                new File(dir.getPath() + "/frameworks_platform_compat_config.xml"));
-
+        writeToFile(dir, "libcore_platform_compat_config.xml", configXml1);
+        writeToFile(dir, "frameworks_platform_compat_config.xml", configXml2);
 
         CompatConfig pc = new CompatConfig();
         pc.initConfigFromLib(dir);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index f54f885..f97c887 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1162,7 +1162,8 @@
                 MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
 
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
-                eq(UserHandle.USER_SYSTEM), eq(null),
+                eq(UserHandle.USER_SYSTEM),
+                eq(null),
                 eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
 
         verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
@@ -1966,7 +1967,6 @@
         // TODO Make sure restrictions are written to the file.
     }
 
-    // TODO: (b/138709470) test addUserRestriction as PO of an organization-owned device
     public void testSetUserRestriction_asPoOfOrgOwnedDevice() throws Exception {
         final int MANAGED_PROFILE_USER_ID = DpmMockContext.CALLER_USER_HANDLE;
         final int MANAGED_PROFILE_ADMIN_UID =
@@ -1979,16 +1979,26 @@
         when(getServices().userManager.getProfileParent(MANAGED_PROFILE_USER_ID))
                 .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
 
+        parentDpm.addUserRestriction(admin1, UserManager.DISALLOW_CONFIG_DATE_TIME);
+        verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
+                eq(MANAGED_PROFILE_USER_ID),
+                MockUtils.checkUserRestrictions(UserManager.DISALLOW_CONFIG_DATE_TIME),
+                eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
+        reset(getServices().userManagerInternal);
+
+        parentDpm.clearUserRestriction(admin1, UserManager.DISALLOW_CONFIG_DATE_TIME);
+        reset(getServices().userManagerInternal);
+
         parentDpm.setCameraDisabled(admin1, true);
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
-                eq(UserHandle.USER_SYSTEM),
+                eq(MANAGED_PROFILE_USER_ID),
                 MockUtils.checkUserRestrictions(UserManager.DISALLOW_CAMERA),
                 eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
         reset(getServices().userManagerInternal);
 
         parentDpm.setCameraDisabled(admin1, false);
         verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
-                eq(UserHandle.USER_SYSTEM),
+                eq(MANAGED_PROFILE_USER_ID),
                 MockUtils.checkUserRestrictions(),
                 eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
         reset(getServices().userManagerInternal);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 7a2350e..919a3f6 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -35,6 +35,7 @@
 import android.app.timezonedetector.TimeZoneDetector;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -162,6 +163,8 @@
 
         // Package manager is huge, so we use a partial mock instead.
         packageManager = spy(realContext.getPackageManager());
+        when(packageManagerInternal.getSystemUiServiceComponent()).thenReturn(
+                new ComponentName("com.android.systemui", ".Service"));
 
         contentResolver = new MockContentResolver();
         contentResolver.addProvider("telephony", new MockContentProvider(realContext) {
diff --git a/services/tests/servicestests/src/com/android/server/om/MockitoUtils.kt b/services/tests/servicestests/src/com/android/server/om/MockitoUtils.kt
new file mode 100644
index 0000000..0f915db
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/om/MockitoUtils.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.om
+
+import org.mockito.Answers
+import org.mockito.Mockito
+import org.mockito.invocation.InvocationOnMock
+import org.mockito.stubbing.Answer
+import org.mockito.stubbing.Stubber
+
+// TODO(chiuwinson): Move this entire file to a shared utility module
+// TODO(b/135203078): De-dupe utils added for overlays vs package refactor
+object MockitoUtils {
+    val ANSWER_THROWS = Answer<Any?> {
+        when (val name = it.method.name) {
+            "toString" -> return@Answer Answers.CALLS_REAL_METHODS.answer(it)
+            else -> {
+                val arguments = it.arguments
+                        ?.takeUnless { it.isEmpty() }
+                        ?.joinToString()
+                        ?.let {
+                            "with $it"
+                        }
+                        .orEmpty()
+
+                throw UnsupportedOperationException("${it.mock::class.java.simpleName}#$name " +
+                        "$arguments should not be called")
+            }
+        }
+    }
+}
+
+inline fun <reified T> mock(block: T.() -> Unit = {}) = Mockito.mock(T::class.java).apply(block)
+
+fun <Type> Stubber.whenever(mock: Type) = Mockito.`when`(mock)
+fun <Type : Any?> whenever(mock: Type) = Mockito.`when`(mock)
+
+@Suppress("UNCHECKED_CAST")
+fun <Type : Any?> whenever(mock: Type, block: InvocationOnMock.() -> Any?) =
+        Mockito.`when`(mock).thenAnswer { block(it) }
+
+fun whenever(mock: Unit) = Mockito.`when`(mock).thenAnswer { }
+
+inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit): T {
+    val swappingAnswer = object : Answer<Any?> {
+        var delegate: Answer<*> = Answers.RETURNS_DEFAULTS
+
+        override fun answer(invocation: InvocationOnMock?): Any? {
+            return delegate.answer(invocation)
+        }
+    }
+
+    return Mockito.mock(T::class.java, swappingAnswer).apply(block)
+            .also {
+                // To allow when() usage inside block, only swap to throwing afterwards
+                swappingAnswer.delegate = MockitoUtils.ANSWER_THROWS
+            }
+}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
new file mode 100644
index 0000000..ef12948
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
@@ -0,0 +1,206 @@
+/*
+ * 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.om
+
+import android.content.pm.parsing.AndroidPackage
+import android.net.Uri
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.testng.Assert.assertThrows
+
+@RunWith(Parameterized::class)
+class OverlayReferenceMapperTests {
+
+    companion object {
+        private const val TARGET_PACKAGE_NAME = "com.test.target"
+        private const val OVERLAY_PACKAGE_NAME = "com.test.overlay"
+        private const val ACTOR_PACKAGE_NAME = "com.test.actor"
+        private const val ACTOR_NAME = "overlay://test/actorName"
+
+        @JvmStatic
+        @Parameterized.Parameters(name = "deferRebuild {0}")
+        fun parameters() = arrayOf(true, false)
+    }
+
+    private lateinit var mapper: OverlayReferenceMapper
+
+    @JvmField
+    @Parameterized.Parameter(0)
+    var deferRebuild = false
+
+    @Before
+    fun initMapper() {
+        mapper = mapper()
+    }
+
+    @Test
+    fun targetWithOverlay() {
+        val target = mockTarget()
+        val overlay = mockOverlay()
+        val existing = mapper.addInOrder(overlay)
+        assertEmpty()
+        mapper.addInOrder(target, existing = existing)
+        assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay))
+        mapper.remove(target)
+        assertEmpty()
+    }
+
+    @Test
+    fun targetWithMultipleOverlays() {
+        val target = mockTarget()
+        val overlay0 = mockOverlay(0)
+        val overlay1 = mockOverlay(1)
+        mapper = mapper(
+                overlayToTargetToOverlayables = mapOf(
+                        overlay0.packageName to mapOf(
+                                target.packageName to target.overlayables.keys
+                        ),
+                        overlay1.packageName to mapOf(
+                                target.packageName to target.overlayables.keys
+                        )
+                )
+        )
+        val existing = mapper.addInOrder(overlay0, overlay1)
+        assertEmpty()
+        mapper.addInOrder(target, existing = existing)
+        assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay0, overlay1))
+        mapper.remove(overlay0)
+        assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay1))
+        mapper.remove(target)
+        assertEmpty()
+    }
+
+    @Test
+    fun targetWithoutOverlay() {
+        val target = mockTarget()
+        mapper.addInOrder(target)
+        assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
+        mapper.remove(target)
+        assertEmpty()
+    }
+
+    @Test
+    fun overlayWithTarget() {
+        val target = mockTarget()
+        val overlay = mockOverlay()
+        val existing = mapper.addInOrder(target)
+        assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
+        mapper.addInOrder(overlay, existing = existing)
+        assertMapping(ACTOR_PACKAGE_NAME to setOf(target, overlay))
+        mapper.remove(overlay)
+        assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
+    }
+
+    @Test
+    fun overlayWithMultipleTargets() {
+        val target0 = mockTarget(0)
+        val target1 = mockTarget(1)
+        val overlay = mockOverlay()
+        mapper = mapper(
+                overlayToTargetToOverlayables = mapOf(
+                        overlay.packageName to mapOf(
+                                target0.packageName to target0.overlayables.keys,
+                                target1.packageName to target1.overlayables.keys
+                        )
+                )
+        )
+        mapper.addInOrder(target0, target1, overlay)
+        assertMapping(ACTOR_PACKAGE_NAME to setOf(target0, target1, overlay))
+        mapper.remove(target0)
+        assertMapping(ACTOR_PACKAGE_NAME to setOf(target1, overlay))
+        mapper.remove(target1)
+        assertEmpty()
+    }
+
+    @Test
+    fun overlayWithoutTarget() {
+        val overlay = mockOverlay()
+        mapper.addInOrder(overlay)
+        // An overlay can only have visibility exposed through its target
+        assertEmpty()
+        mapper.remove(overlay)
+        assertEmpty()
+    }
+
+    private fun OverlayReferenceMapper.addInOrder(
+        vararg pkgs: AndroidPackage,
+        existing: MutableMap<String, AndroidPackage> = mutableMapOf()
+    ) = pkgs.fold(existing) { map, pkg ->
+        addPkg(pkg, map)
+        map[pkg.packageName] = pkg
+        return@fold map
+    }
+
+    private fun OverlayReferenceMapper.remove(pkg: AndroidPackage) = removePkg(pkg.packageName)
+
+    private fun assertMapping(vararg pairs: Pair<String, Set<AndroidPackage>>) {
+        val expected = pairs.associate { it }
+                .mapValues { pair -> pair.value.map { it.packageName }.toSet() }
+
+        // This validates the API exposed for querying the relationships
+        expected.forEach { (actorPkgName, expectedPkgNames) ->
+            expectedPkgNames.forEach { expectedPkgName ->
+                if (deferRebuild) {
+                    assertThrows(IllegalStateException::class.java) {
+                        mapper.isValidActor(expectedPkgName, actorPkgName)
+                    }
+                    mapper.rebuildIfDeferred()
+                    deferRebuild = false
+                }
+
+                assertThat(mapper.isValidActor(expectedPkgName, actorPkgName)).isTrue()
+            }
+        }
+
+        // This asserts no other relationships are defined besides those tested above
+        assertThat(mapper.actorPkgToPkgs).containsExactlyEntriesIn(expected)
+    }
+
+    private fun assertEmpty() = assertMapping()
+
+    private fun mapper(
+        namedActors: Map<String, Map<String, String>> = Uri.parse(ACTOR_NAME).run {
+            mapOf(authority!! to mapOf(pathSegments.first() to ACTOR_PACKAGE_NAME))
+        },
+        overlayToTargetToOverlayables: Map<String, Map<String, Set<String>>> = mapOf(
+                mockOverlay().packageName to mapOf(
+                        mockTarget().run { packageName to overlayables.keys }
+                )
+        )
+    ) = OverlayReferenceMapper(deferRebuild, object : OverlayReferenceMapper.Provider {
+        override fun getActorPkg(actor: String?) =
+                OverlayActorEnforcer.getPackageNameForActor(actor, namedActors).first
+
+        override fun getTargetToOverlayables(pkg: AndroidPackage) =
+                overlayToTargetToOverlayables[pkg.packageName] ?: emptyMap()
+    })
+
+    private fun mockTarget(increment: Int = 0) = mockThrowOnUnmocked<AndroidPackage> {
+        whenever(packageName) { "$TARGET_PACKAGE_NAME$increment" }
+        whenever(overlayables) { mapOf("overlayableName$increment" to ACTOR_NAME) }
+        whenever(toString()) { "Package{$packageName}" }
+    }
+
+    private fun mockOverlay(increment: Int = 0) = mockThrowOnUnmocked<AndroidPackage> {
+        whenever(packageName) { "$OVERLAY_PACKAGE_NAME$increment" }
+        whenever(overlayables) { emptyMap<String, String>() }
+        whenever(toString()) { "Package{$packageName}" }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 4fc625a..82bbdcb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -35,6 +35,11 @@
 import android.os.Build;
 import android.os.Process;
 import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import androidx.annotation.NonNull;
+
+import com.android.server.om.OverlayReferenceMapper;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -43,11 +48,18 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
 @RunWith(JUnit4.class)
 public class AppsFilterTest {
 
     private static final int DUMMY_CALLING_UID = 10345;
     private static final int DUMMY_TARGET_UID = 10556;
+    private static final int DUMMY_ACTOR_UID = 10656;
+    private static final int DUMMY_OVERLAY_UID = 10756;
+    private static final int DUMMY_ACTOR_TWO_UID = 10856;
 
     @Mock
     AppsFilter.FeatureConfig mFeatureConfigMock;
@@ -117,7 +129,7 @@
     @Test
     public void testSystemReadyPropogates() throws Exception {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
         appsFilter.onSystemReady();
         verify(mFeatureConfigMock).onSystemReady();
     }
@@ -125,7 +137,8 @@
     @Test
     public void testQueriesAction_FilterMatches() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+        appsFilter.onSystemReady();
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package", new IntentFilter("TEST_ACTION")), DUMMY_TARGET_UID);
@@ -138,7 +151,8 @@
     @Test
     public void testQueriesAction_NoMatchingAction_Filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+        appsFilter.onSystemReady();
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -151,7 +165,8 @@
     @Test
     public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+        appsFilter.onSystemReady();
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -169,7 +184,8 @@
     @Test
     public void testNoQueries_Filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+        appsFilter.onSystemReady();
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -182,7 +198,8 @@
     @Test
     public void testForceQueryable_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+        appsFilter.onSystemReady();
 
         PackageSetting target = simulateAddPackage(appsFilter,
                         pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_UID);
@@ -195,7 +212,8 @@
     @Test
     public void testForceQueryableByDevice_SystemCaller_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false);
+                new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null);
+        appsFilter.onSystemReady();
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID,
@@ -209,7 +227,8 @@
     @Test
     public void testForceQueryableByDevice_NonSystemCaller_Filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false);
+                new AppsFilter(mFeatureConfigMock, new String[]{"com.some.package"}, false, null);
+        appsFilter.onSystemReady();
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -224,7 +243,8 @@
     public void testSystemQueryable_DoesntFilter() {
         final AppsFilter appsFilter =
                 new AppsFilter(mFeatureConfigMock, new String[]{},
-                        true /* system force queryable */);
+                        true /* system force queryable */, null);
+        appsFilter.onSystemReady();
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID,
@@ -238,7 +258,8 @@
     @Test
     public void testQueriesPackage_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+        appsFilter.onSystemReady();
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -253,7 +274,8 @@
         when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class)))
                 .thenReturn(false);
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+        appsFilter.onSystemReady();
 
         PackageSetting target = simulateAddPackage(
                 appsFilter, pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -266,20 +288,22 @@
     @Test
     public void testSystemUid_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+        appsFilter.onSystemReady();
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
 
         assertFalse(appsFilter.shouldFilterApplication(0, null, target, 0));
-        assertFalse(appsFilter.shouldFilterApplication(
-                Process.FIRST_APPLICATION_UID - 1, null, target, 0));
+        assertFalse(appsFilter.shouldFilterApplication(Process.FIRST_APPLICATION_UID - 1,
+                null, target, 0));
     }
 
     @Test
     public void testNonSystemUid_NoCallingSetting_Filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+        appsFilter.onSystemReady();
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package"), DUMMY_TARGET_UID);
@@ -290,7 +314,8 @@
     @Test
     public void testNoTargetPackage_filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mFeatureConfigMock, new String[]{}, false);
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+        appsFilter.onSystemReady();
 
         PackageSetting target = new PackageSettingBuilder()
                 .setName("com.some.package")
@@ -304,6 +329,127 @@
         assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
     }
 
+    @Test
+    public void testActsOnTargetOfOverlay() {
+        final String actorName = "overlay://test/actorName";
+
+        ParsingPackage target = pkg("com.some.package.target")
+                .addOverlayable("overlayableName", actorName);
+        ParsingPackage overlay = pkg("com.some.package.overlay")
+                .setIsOverlay(true)
+                .setOverlayTarget(target.getPackageName())
+                .setOverlayTargetName("overlayableName");
+        ParsingPackage actor = pkg("com.some.package.actor");
+
+        final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false,
+                new OverlayReferenceMapper.Provider() {
+                    @Nullable
+                    @Override
+                    public String getActorPkg(String actorString) {
+                        if (actorName.equals(actorString)) {
+                            return actor.getPackageName();
+                        }
+                        return null;
+                    }
+
+                    @NonNull
+                    @Override
+                    public Map<String, Set<String>> getTargetToOverlayables(
+                            @NonNull AndroidPackage pkg) {
+                        if (overlay.getPackageName().equals(pkg.getPackageName())) {
+                            Map<String, Set<String>> map = new ArrayMap<>();
+                            Set<String> set = new ArraySet<>();
+                            set.add(overlay.getOverlayTargetName());
+                            map.put(overlay.getOverlayTarget(), set);
+                            return map;
+                        }
+                        return Collections.emptyMap();
+                    }
+                });
+        appsFilter.onSystemReady();
+
+        PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID);
+        PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID);
+        PackageSetting actorSetting = simulateAddPackage(appsFilter, actor, DUMMY_ACTOR_UID);
+
+        // Actor can see both target and overlay
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting,
+                targetSetting, 0));
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_UID, actorSetting,
+                overlaySetting, 0));
+
+        // But target/overlay can't see each other
+        assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting,
+                overlaySetting, 0));
+        assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting,
+                targetSetting, 0));
+
+        // And can't see the actor
+        assertTrue(appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, targetSetting,
+                actorSetting, 0));
+        assertTrue(appsFilter.shouldFilterApplication(DUMMY_OVERLAY_UID, overlaySetting,
+                actorSetting, 0));
+    }
+
+    @Test
+    public void testActsOnTargetOfOverlayThroughSharedUser() {
+        final String actorName = "overlay://test/actorName";
+
+        ParsingPackage target = pkg("com.some.package.target")
+                .addOverlayable("overlayableName", actorName);
+        ParsingPackage overlay = pkg("com.some.package.overlay")
+                .setIsOverlay(true)
+                .setOverlayTarget(target.getPackageName())
+                .setOverlayTargetName("overlayableName");
+        ParsingPackage actorOne = pkg("com.some.package.actor.one");
+        ParsingPackage actorTwo = pkg("com.some.package.actor.two");
+
+        final AppsFilter appsFilter = new AppsFilter(mFeatureConfigMock, new String[]{}, false,
+                new OverlayReferenceMapper.Provider() {
+                    @Nullable
+                    @Override
+                    public String getActorPkg(String actorString) {
+                        // Only actorOne is mapped as a valid actor
+                        if (actorName.equals(actorString)) {
+                            return actorOne.getPackageName();
+                        }
+                        return null;
+                    }
+
+                    @NonNull
+                    @Override
+                    public Map<String, Set<String>> getTargetToOverlayables(
+                            @NonNull AndroidPackage pkg) {
+                        if (overlay.getPackageName().equals(pkg.getPackageName())) {
+                            Map<String, Set<String>> map = new ArrayMap<>();
+                            Set<String> set = new ArraySet<>();
+                            set.add(overlay.getOverlayTargetName());
+                            map.put(overlay.getOverlayTarget(), set);
+                            return map;
+                        }
+                        return Collections.emptyMap();
+                    }
+                });
+        appsFilter.onSystemReady();
+
+        PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_UID);
+        PackageSetting overlaySetting = simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_UID);
+        PackageSetting actorOneSetting = simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_UID);
+        PackageSetting actorTwoSetting = simulateAddPackage(appsFilter, actorTwo,
+                DUMMY_ACTOR_TWO_UID);
+
+        SharedUserSetting actorSharedSetting = new SharedUserSetting("actorSharedUser",
+                actorOneSetting.pkgFlags, actorOneSetting.pkgPrivateFlags);
+        actorSharedSetting.addPackage(actorOneSetting);
+        actorSharedSetting.addPackage(actorTwoSetting);
+
+        // actorTwo can see both target and overlay
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting,
+                targetSetting, 0));
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_ACTOR_TWO_UID, actorSharedSetting,
+                overlaySetting, 0));
+    }
+
     private interface WithSettingBuilder {
         PackageSettingBuilder withBuilder(PackageSettingBuilder builder);
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index b751308..c478ec4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -173,6 +173,7 @@
                 /* createdMillis */ 0L,
                 /* stageDir */ mTmpDir,
                 /* stageCid */ null,
+                /* files */ null,
                 /* prepared */ true,
                 /* committed */ true,
                 /* sealed */ false,  // Setting to true would trigger some PM logic.
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 5baeede..2473997 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -22,7 +22,7 @@
 
 import java.io.File;
 
-class PackageSettingBuilder {
+public class PackageSettingBuilder {
     private String mName;
     private String mRealName;
     private String mCodePath;
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
new file mode 100644
index 0000000..5a2ce45
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import static org.junit.Assert.assertEquals;
+
+import android.hardware.audio.common.V2_0.Uuid;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ConversionUtilTest {
+    private static final String TAG = "ConversionUtilTest";
+
+    @Test
+    public void testUuidRoundTrip() {
+        Uuid hidl = new Uuid();
+        hidl.timeLow = 0xFEDCBA98;
+        hidl.timeMid = (short) 0xEDCB;
+        hidl.versionAndTimeHigh = (short) 0xDCBA;
+        hidl.variantAndClockSeqHigh = (short) 0xCBA9;
+        hidl.node = new byte[] { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 };
+
+        String aidl = ConversionUtil.hidl2aidlUuid(hidl);
+        assertEquals("fedcba98-edcb-dcba-cba9-111213141516", aidl);
+
+        Uuid reconstructed = ConversionUtil.aidl2hidlUuid(aidl);
+        assertEquals(hidl, reconstructed);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
new file mode 100644
index 0000000..82f32f8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -0,0 +1,1306 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.audio.common.V2_0.AudioConfig;
+import android.hardware.audio.common.V2_0.Uuid;
+import android.hardware.soundtrigger.V2_3.OptionalModelParameterRange;
+import android.media.audio.common.AudioChannelMask;
+import android.media.audio.common.AudioFormat;
+import android.media.soundtrigger_middleware.ConfidenceLevel;
+import android.media.soundtrigger_middleware.ISoundTriggerCallback;
+import android.media.soundtrigger_middleware.ISoundTriggerModule;
+import android.media.soundtrigger_middleware.ModelParameter;
+import android.media.soundtrigger_middleware.ModelParameterRange;
+import android.media.soundtrigger_middleware.Phrase;
+import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
+import android.media.soundtrigger_middleware.PhraseSoundModel;
+import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger_middleware.RecognitionMode;
+import android.media.soundtrigger_middleware.RecognitionStatus;
+import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger_middleware.SoundModelType;
+import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
+import android.os.HidlMemoryUtil;
+import android.os.HwParcel;
+import android.os.IHwBinder;
+import android.os.IHwInterface;
+import android.os.RemoteException;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.stubbing.Answer;
+
+@RunWith(Parameterized.class)
+public class SoundTriggerMiddlewareImplTest {
+    private static final String TAG = "SoundTriggerMiddlewareImplTest";
+
+    // We run the test once for every version of the underlying driver.
+    @Parameterized.Parameters
+    public static Object[] data() {
+        return new Object[]{
+                mock(android.hardware.soundtrigger.V2_0.ISoundTriggerHw.class),
+                mock(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.class),
+                mock(android.hardware.soundtrigger.V2_2.ISoundTriggerHw.class),
+                mock(android.hardware.soundtrigger.V2_3.ISoundTriggerHw.class),
+        };
+    }
+
+    @Mock
+    @Parameterized.Parameter
+    public android.hardware.soundtrigger.V2_0.ISoundTriggerHw mHalDriver;
+
+    @Mock
+    private SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider = mock(
+            SoundTriggerMiddlewareImpl.AudioSessionProvider.class);
+
+    private SoundTriggerMiddlewareImpl mService;
+
+    private static ISoundTriggerCallback createCallbackMock() {
+        return mock(ISoundTriggerCallback.Stub.class, Mockito.CALLS_REAL_METHODS);
+    }
+
+    private static SoundModel createGenericSoundModel() {
+        return createSoundModel(SoundModelType.GENERIC);
+    }
+
+    private static SoundModel createSoundModel(int type) {
+        SoundModel model = new SoundModel();
+        model.type = type;
+        model.uuid = "12345678-2345-3456-4567-abcdef987654";
+        model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
+        model.data = new byte[]{91, 92, 93, 94, 95};
+        return model;
+    }
+
+    private static PhraseSoundModel createPhraseSoundModel() {
+        PhraseSoundModel model = new PhraseSoundModel();
+        model.common = createSoundModel(SoundModelType.KEYPHRASE);
+        model.phrases = new Phrase[1];
+        model.phrases[0] = new Phrase();
+        model.phrases[0].id = 123;
+        model.phrases[0].users = new int[]{5, 6, 7};
+        model.phrases[0].locale = "locale";
+        model.phrases[0].text = "text";
+        model.phrases[0].recognitionModes =
+                RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION;
+        return model;
+    }
+
+    private static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties createDefaultProperties(
+            boolean supportConcurrentCapture) {
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
+                new android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties();
+        properties.implementor = "implementor";
+        properties.description = "description";
+        properties.version = 123;
+        properties.uuid = new Uuid();
+        properties.uuid.timeLow = 1;
+        properties.uuid.timeMid = 2;
+        properties.uuid.versionAndTimeHigh = 3;
+        properties.uuid.variantAndClockSeqHigh = 4;
+        properties.uuid.node = new byte[]{5, 6, 7, 8, 9, 10};
+
+        properties.maxSoundModels = 456;
+        properties.maxKeyPhrases = 567;
+        properties.maxUsers = 678;
+        properties.recognitionModes = 789;
+        properties.captureTransition = true;
+        properties.maxBufferMs = 321;
+        properties.concurrentCapture = supportConcurrentCapture;
+        properties.triggerInEvent = true;
+        properties.powerConsumptionMw = 432;
+        return properties;
+    }
+
+    private static void validateDefaultProperties(SoundTriggerModuleProperties properties,
+            boolean supportConcurrentCapture) {
+        assertEquals("implementor", properties.implementor);
+        assertEquals("description", properties.description);
+        assertEquals(123, properties.version);
+        assertEquals("00000001-0002-0003-0004-05060708090a", properties.uuid);
+        assertEquals(456, properties.maxSoundModels);
+        assertEquals(567, properties.maxKeyPhrases);
+        assertEquals(678, properties.maxUsers);
+        assertEquals(789, properties.recognitionModes);
+        assertTrue(properties.captureTransition);
+        assertEquals(321, properties.maxBufferMs);
+        assertEquals(supportConcurrentCapture, properties.concurrentCapture);
+        assertTrue(properties.triggerInEvent);
+        assertEquals(432, properties.powerConsumptionMw);
+    }
+
+
+    private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
+            int hwHandle,
+            int status) {
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
+                new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
+        halEvent.status = status;
+        halEvent.type = SoundModelType.GENERIC;
+        halEvent.model = hwHandle;
+        halEvent.captureAvailable = true;
+        // This field is ignored.
+        halEvent.captureSession = 123;
+        halEvent.captureDelayMs = 234;
+        halEvent.capturePreambleMs = 345;
+        halEvent.triggerInData = true;
+        halEvent.audioConfig = new AudioConfig();
+        halEvent.audioConfig.sampleRateHz = 456;
+        halEvent.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
+        halEvent.audioConfig.format = AudioFormat.MP3;
+        // hwEvent.audioConfig.offloadInfo is irrelevant.
+        halEvent.data.add((byte) 31);
+        halEvent.data.add((byte) 32);
+        halEvent.data.add((byte) 33);
+        return halEvent;
+    }
+
+    private static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
+            int hwHandle,
+            int status) {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent halEvent =
+                new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent();
+        halEvent.header = createRecognitionEvent_2_0(hwHandle, status);
+        halEvent.header.data.clear();
+        halEvent.data = HidlMemoryUtil.byteArrayToHidlMemory(new byte[]{31, 32, 33});
+        return halEvent;
+    }
+
+    private static void validateRecognitionEvent(RecognitionEvent event, int status) {
+        assertEquals(status, event.status);
+        assertEquals(SoundModelType.GENERIC, event.type);
+        assertTrue(event.captureAvailable);
+        assertEquals(101, event.captureSession);
+        assertEquals(234, event.captureDelayMs);
+        assertEquals(345, event.capturePreambleMs);
+        assertTrue(event.triggerInData);
+        assertEquals(456, event.audioConfig.sampleRateHz);
+        assertEquals(AudioChannelMask.IN_LEFT, event.audioConfig.channelMask);
+        assertEquals(AudioFormat.MP3, event.audioConfig.format);
+    }
+
+    private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0(
+            int hwHandle, int status) {
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
+                new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
+        halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
+
+        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
+                new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
+        halExtra.id = 123;
+        halExtra.confidenceLevel = 52;
+        halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+                | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
+                new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
+        halLevel.userId = 31;
+        halLevel.levelPercent = 43;
+        halExtra.levels.add(halLevel);
+        halEvent.phraseExtras.add(halExtra);
+        return halEvent;
+    }
+
+    private static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_1(
+            int hwHandle, int status) {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
+                new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent();
+        halEvent.common = createRecognitionEvent_2_1(hwHandle, status);
+
+        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
+                new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
+        halExtra.id = 123;
+        halExtra.confidenceLevel = 52;
+        halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+                | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
+                new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
+        halLevel.userId = 31;
+        halLevel.levelPercent = 43;
+        halExtra.levels.add(halLevel);
+        halEvent.phraseExtras.add(halExtra);
+        return halEvent;
+    }
+
+    private static void validatePhraseRecognitionEvent(PhraseRecognitionEvent event, int status) {
+        validateRecognitionEvent(event.common, status);
+
+        assertEquals(1, event.phraseExtras.length);
+        assertEquals(123, event.phraseExtras[0].id);
+        assertEquals(52, event.phraseExtras[0].confidenceLevel);
+        assertEquals(RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER,
+                event.phraseExtras[0].recognitionModes);
+        assertEquals(1, event.phraseExtras[0].levels.length);
+        assertEquals(31, event.phraseExtras[0].levels[0].userId);
+        assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
+    }
+
+    private void initService(boolean supportConcurrentCapture) throws RemoteException {
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
+                    createDefaultProperties(
+                            supportConcurrentCapture);
+            ((android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback) invocation.getArgument(
+                    0)).onValues(0,
+                    properties);
+            return null;
+        }).when(mHalDriver).getProperties(any());
+        mService = new SoundTriggerMiddlewareImpl(mHalDriver, mAudioSessionProvider);
+    }
+
+    private int loadGenericModel_2_0(ISoundTriggerModule module, int hwHandle)
+            throws RemoteException {
+        SoundModel model = createGenericSoundModel();
+        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel> modelCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel.class);
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
+                    invocation.getArgument(1);
+            int callbackCookie = invocation.getArgument(2);
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadSoundModelCallback
+                    resultCallback = invocation.getArgument(3);
+
+            // This is the return of this method.
+            resultCallback.onValues(0, hwHandle);
+
+            // This is the async mCallback that comes after.
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent modelEvent =
+                    new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent();
+            modelEvent.status =
+                    android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
+            modelEvent.model = hwHandle;
+            callback.soundModelCallback(modelEvent, callbackCookie);
+            return null;
+        }).when(mHalDriver).loadSoundModel(modelCaptor.capture(), any(), anyInt(), any());
+
+        when(mAudioSessionProvider.acquireSession()).thenReturn(
+                new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
+
+        int handle = module.loadModel(model);
+        verify(mHalDriver).loadSoundModel(any(), any(), anyInt(), any());
+        verify(mAudioSessionProvider).acquireSession();
+
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel hidlModel =
+                modelCaptor.getValue();
+        assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC,
+                hidlModel.type);
+        assertEquals(model.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.uuid));
+        assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.vendorUuid));
+        assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.data.toArray());
+
+        return handle;
+    }
+
+    private int loadGenericModel_2_1(ISoundTriggerModule module, int hwHandle)
+            throws RemoteException {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
+                (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+        SoundModel model = createGenericSoundModel();
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
+                    invocation.getArgument(1);
+            int callbackCookie = invocation.getArgument(2);
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
+                    resultCallback = invocation.getArgument(3);
+
+            // This is the return of this method.
+            resultCallback.onValues(0, hwHandle);
+
+            // This is the async mCallback that comes after.
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent modelEvent =
+                    new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent();
+            modelEvent.header.status =
+                    android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
+            modelEvent.header.model = hwHandle;
+            callback.soundModelCallback_2_1(modelEvent, callbackCookie);
+            return null;
+        }).when(driver).loadSoundModel_2_1(modelCaptor.capture(), any(), anyInt(), any());
+
+        when(mAudioSessionProvider.acquireSession()).thenReturn(
+                new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
+
+        int handle = module.loadModel(model);
+        verify(driver).loadSoundModel_2_1(any(), any(), anyInt(), any());
+        verify(mAudioSessionProvider).acquireSession();
+
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel hidlModel =
+                modelCaptor.getValue();
+        assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC,
+                hidlModel.header.type);
+        assertEquals(model.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.header.uuid));
+        assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.header.vendorUuid));
+        assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
+                HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.data));
+
+        return handle;
+    }
+
+    private int loadGenericModel(ISoundTriggerModule module, int hwHandle) throws RemoteException {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+            return loadGenericModel_2_1(module, hwHandle);
+        } else {
+            return loadGenericModel_2_0(module, hwHandle);
+        }
+    }
+
+    private int loadPhraseModel_2_0(ISoundTriggerModule module, int hwHandle)
+            throws RemoteException {
+        PhraseSoundModel model = createPhraseSoundModel();
+        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel>
+                modelCaptor = ArgumentCaptor.forClass(
+                android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel.class);
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
+                    invocation.getArgument(
+                            1);
+            int callbackCookie = invocation.getArgument(2);
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadPhraseSoundModelCallback
+                    resultCallback =
+                    invocation.getArgument(
+                            3);
+
+            // This is the return of this method.
+            resultCallback.onValues(0, hwHandle);
+
+            // This is the async mCallback that comes after.
+            android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent modelEvent =
+                    new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent();
+            modelEvent.status =
+                    android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
+            modelEvent.model = hwHandle;
+            callback.soundModelCallback(modelEvent, callbackCookie);
+            return null;
+        }).when(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), any(), anyInt(), any());
+
+        when(mAudioSessionProvider.acquireSession()).thenReturn(
+                new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
+
+        int handle = module.loadPhraseModel(model);
+        verify(mHalDriver).loadPhraseSoundModel(any(), any(), anyInt(), any());
+        verify(mAudioSessionProvider).acquireSession();
+
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel hidlModel =
+                modelCaptor.getValue();
+
+        // Validate common part.
+        assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE,
+                hidlModel.common.type);
+        assertEquals(model.common.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.common.uuid));
+        assertEquals(model.common.vendorUuid,
+                ConversionUtil.hidl2aidlUuid(hidlModel.common.vendorUuid));
+        assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.common.data.toArray());
+
+        // Validate phrase part.
+        assertEquals(1, hidlModel.phrases.size());
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Phrase hidlPhrase =
+                hidlModel.phrases.get(0);
+        assertEquals(123, hidlPhrase.id);
+        assertArrayEquals(new Integer[]{5, 6, 7}, hidlPhrase.users.toArray());
+        assertEquals("locale", hidlPhrase.locale);
+        assertEquals("text", hidlPhrase.text);
+        assertEquals(android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
+                        | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
+                hidlPhrase.recognitionModes);
+
+        return handle;
+    }
+
+    private int loadPhraseModel_2_1(ISoundTriggerModule module, int hwHandle)
+            throws RemoteException {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
+                (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+        PhraseSoundModel model = createPhraseSoundModel();
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
+                modelCaptor = ArgumentCaptor.forClass(
+                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
+                    invocation.getArgument(
+                            1);
+            int callbackCookie = invocation.getArgument(2);
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
+                    resultCallback =
+                    invocation.getArgument(
+                            3);
+
+            // This is the return of this method.
+            resultCallback.onValues(0, hwHandle);
+
+            // This is the async mCallback that comes after.
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent modelEvent =
+                    new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent();
+            modelEvent.header.status =
+                    android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
+            modelEvent.header.model = hwHandle;
+            callback.soundModelCallback_2_1(modelEvent, callbackCookie);
+            return null;
+        }).when(driver).loadPhraseSoundModel_2_1(modelCaptor.capture(), any(), anyInt(), any());
+
+        when(mAudioSessionProvider.acquireSession()).thenReturn(
+                new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
+
+        int handle = module.loadPhraseModel(model);
+        verify(driver).loadPhraseSoundModel_2_1(any(), any(), anyInt(), any());
+        verify(mAudioSessionProvider).acquireSession();
+
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel hidlModel =
+                modelCaptor.getValue();
+
+        // Validate common part.
+        assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE,
+                hidlModel.common.header.type);
+        assertEquals(model.common.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.common.header.uuid));
+        assertEquals(model.common.vendorUuid,
+                ConversionUtil.hidl2aidlUuid(hidlModel.common.header.vendorUuid));
+        assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
+                HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.common.data));
+
+        // Validate phrase part.
+        assertEquals(1, hidlModel.phrases.size());
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Phrase hidlPhrase =
+                hidlModel.phrases.get(0);
+        assertEquals(123, hidlPhrase.id);
+        assertArrayEquals(new Integer[]{5, 6, 7}, hidlPhrase.users.toArray());
+        assertEquals("locale", hidlPhrase.locale);
+        assertEquals("text", hidlPhrase.text);
+        assertEquals(android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
+                        | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
+                hidlPhrase.recognitionModes);
+
+        return handle;
+    }
+
+    private int loadPhraseModel(ISoundTriggerModule module, int hwHandle) throws RemoteException {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+            return loadPhraseModel_2_1(module, hwHandle);
+        } else {
+            return loadPhraseModel_2_0(module, hwHandle);
+        }
+    }
+
+    private void unloadModel(ISoundTriggerModule module, int handle, int hwHandle)
+            throws RemoteException {
+        module.unloadModel(handle);
+        verify(mHalDriver).unloadSoundModel(hwHandle);
+        verify(mAudioSessionProvider).releaseSession(101);
+    }
+
+    private SoundTriggerHwCallback startRecognition_2_0(ISoundTriggerModule module, int handle,
+            int hwHandle) throws RemoteException {
+        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig>
+                configCaptor = ArgumentCaptor.forClass(
+                android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
+
+        when(mHalDriver.startRecognition(eq(hwHandle), configCaptor.capture(),
+                callbackCaptor.capture(), cookieCaptor.capture())).thenReturn(0);
+
+        RecognitionConfig config = createRecognitionConfig();
+
+        module.startRecognition(handle, config);
+        verify(mHalDriver).startRecognition(eq(hwHandle), any(), any(), anyInt());
+
+        android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig halConfig =
+                configCaptor.getValue();
+        assertTrue(halConfig.captureRequested);
+        assertEquals(102, halConfig.captureHandle);
+        assertEquals(103, halConfig.captureDevice);
+        assertEquals(1, halConfig.phrases.size());
+        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
+                halConfig.phrases.get(0);
+        assertEquals(123, halPhraseExtra.id);
+        assertEquals(4, halPhraseExtra.confidenceLevel);
+        assertEquals(5, halPhraseExtra.recognitionModes);
+        assertEquals(1, halPhraseExtra.levels.size());
+        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
+        assertEquals(234, halLevel.userId);
+        assertEquals(34, halLevel.levelPercent);
+        assertArrayEquals(new Byte[]{5, 4, 3, 2, 1}, halConfig.data.toArray());
+
+        return new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue());
+    }
+
+    private SoundTriggerHwCallback startRecognition_2_1(ISoundTriggerModule module, int handle,
+            int hwHandle) throws RemoteException {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
+                (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig>
+                configCaptor = ArgumentCaptor.forClass(
+                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig.class);
+        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
+
+        when(driver.startRecognition_2_1(eq(hwHandle), configCaptor.capture(),
+                callbackCaptor.capture(), cookieCaptor.capture())).thenReturn(0);
+
+        RecognitionConfig config = createRecognitionConfig();
+
+        module.startRecognition(handle, config);
+        verify(driver).startRecognition_2_1(eq(hwHandle), any(), any(), anyInt());
+
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig halConfig =
+                configCaptor.getValue();
+        assertTrue(halConfig.header.captureRequested);
+        assertEquals(102, halConfig.header.captureHandle);
+        assertEquals(103, halConfig.header.captureDevice);
+        assertEquals(1, halConfig.header.phrases.size());
+        android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
+                halConfig.header.phrases.get(0);
+        assertEquals(123, halPhraseExtra.id);
+        assertEquals(4, halPhraseExtra.confidenceLevel);
+        assertEquals(5, halPhraseExtra.recognitionModes);
+        assertEquals(1, halPhraseExtra.levels.size());
+        android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
+        assertEquals(234, halLevel.userId);
+        assertEquals(34, halLevel.levelPercent);
+        assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
+                HidlMemoryUtil.hidlMemoryToByteArray(halConfig.data));
+
+        return new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue());
+    }
+
+    private SoundTriggerHwCallback startRecognition(ISoundTriggerModule module, int handle,
+            int hwHandle) throws RemoteException {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+            return startRecognition_2_1(module, handle, hwHandle);
+        } else {
+            return startRecognition_2_0(module, handle, hwHandle);
+        }
+    }
+
+    private RecognitionConfig createRecognitionConfig() {
+        RecognitionConfig config = new RecognitionConfig();
+        config.captureRequested = true;
+        config.phraseRecognitionExtras = new PhraseRecognitionExtra[]{new PhraseRecognitionExtra()};
+        config.phraseRecognitionExtras[0].id = 123;
+        config.phraseRecognitionExtras[0].confidenceLevel = 4;
+        config.phraseRecognitionExtras[0].recognitionModes = 5;
+        config.phraseRecognitionExtras[0].levels = new ConfidenceLevel[]{new ConfidenceLevel()};
+        config.phraseRecognitionExtras[0].levels[0].userId = 234;
+        config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
+        config.data = new byte[]{5, 4, 3, 2, 1};
+        return config;
+    }
+
+    private void stopRecognition(ISoundTriggerModule module, int handle, int hwHandle)
+            throws RemoteException {
+        when(mHalDriver.stopRecognition(hwHandle)).thenReturn(0);
+        module.stopRecognition(handle);
+        verify(mHalDriver).stopRecognition(hwHandle);
+    }
+
+    private void verifyNotStartRecognition() throws RemoteException {
+        verify(mHalDriver, never()).startRecognition(anyInt(), any(), any(), anyInt());
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+            verify((android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver,
+                    never()).startRecognition_2_1(anyInt(), any(), any(), anyInt());
+        }
+    }
+
+
+    @Before
+    public void setUp() throws Exception {
+        clearInvocations(mHalDriver);
+        clearInvocations(mAudioSessionProvider);
+
+        // This binder is associated with the mock, so it can be cast to either version of the
+        // HAL interface.
+        final IHwBinder binder = new IHwBinder() {
+            @Override
+            public void transact(int code, HwParcel request, HwParcel reply, int flags)
+                    throws RemoteException {
+                // This is a little hacky, but a very easy way to gracefully reject a request for
+                // an unsupported interface (after queryLocalInterface() returns null, the client
+                // will attempt a remote transaction to obtain the interface. RemoteException will
+                // cause it to give up).
+                throw new RemoteException();
+            }
+
+            @Override
+            public IHwInterface queryLocalInterface(String descriptor) {
+                if (descriptor.equals("android.hardware.soundtrigger@2.0::ISoundTriggerHw")
+                        || descriptor.equals("android.hardware.soundtrigger@2.1::ISoundTriggerHw")
+                        && mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw
+                        || descriptor.equals("android.hardware.soundtrigger@2.2::ISoundTriggerHw")
+                        && mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw
+                        || descriptor.equals("android.hardware.soundtrigger@2.3::ISoundTriggerHw")
+                        && mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+                    return mHalDriver;
+                }
+                return null;
+            }
+
+            @Override
+            public boolean linkToDeath(DeathRecipient recipient, long cookie) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public boolean unlinkToDeath(DeathRecipient recipient) {
+                throw new UnsupportedOperationException();
+            }
+        };
+
+        when(mHalDriver.asBinder()).thenReturn(binder);
+    }
+
+    @Test
+    public void testSetUpAndTearDown() {
+    }
+
+    @Test
+    public void testListModules() throws Exception {
+        initService(true);
+        // Note: input and output properties are NOT the same type, even though they are in any way
+        // equivalent. One is a type that's exposed by the HAL and one is a type that's exposed by
+        // the service. The service actually performs a (trivial) conversion between the two.
+        SoundTriggerModuleDescriptor[] allDescriptors = mService.listModules();
+        assertEquals(1, allDescriptors.length);
+
+        SoundTriggerModuleProperties properties = allDescriptors[0].properties;
+
+        validateDefaultProperties(properties, true);
+    }
+
+    @Test
+    public void testAttachDetach() throws Exception {
+        // Normal attachment / detachment.
+        initService(true);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+        verify(callback).onRecognitionAvailabilityChange(true);
+        assertNotNull(module);
+        module.detach();
+    }
+
+    @Test
+    public void testAttachDetachNotAvailable() throws Exception {
+        // Attachment / detachment during external capture, with a module not supporting concurrent
+        // capture.
+        initService(false);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+        verify(callback).onRecognitionAvailabilityChange(false);
+        assertNotNull(module);
+        module.detach();
+    }
+
+    @Test
+    public void testAttachDetachAvailable() throws Exception {
+        // Attachment / detachment during external capture, with a module supporting concurrent
+        // capture.
+        initService(true);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+        verify(callback).onRecognitionAvailabilityChange(true);
+        assertNotNull(module);
+        module.detach();
+    }
+
+    @Test
+    public void testLoadUnloadModel() throws Exception {
+        initService(true);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+
+        final int hwHandle = 7;
+        int handle = loadGenericModel(module, hwHandle);
+        unloadModel(module, handle, hwHandle);
+        module.detach();
+    }
+
+    @Test
+    public void testLoadUnloadPhraseModel() throws Exception {
+        initService(true);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+
+        final int hwHandle = 73;
+        int handle = loadPhraseModel(module, hwHandle);
+        unloadModel(module, handle, hwHandle);
+        module.detach();
+    }
+
+    @Test
+    public void testStartStopRecognition() throws Exception {
+        initService(true);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+
+        // Load the model.
+        final int hwHandle = 7;
+        int handle = loadGenericModel(module, hwHandle);
+
+        // Initiate a recognition.
+        startRecognition(module, handle, hwHandle);
+
+        // Stop the recognition.
+        stopRecognition(module, handle, hwHandle);
+
+        // Unload the model.
+        unloadModel(module, handle, hwHandle);
+        module.detach();
+    }
+
+    @Test
+    public void testStartStopPhraseRecognition() throws Exception {
+        initService(true);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+
+        // Load the model.
+        final int hwHandle = 67;
+        int handle = loadPhraseModel(module, hwHandle);
+
+        // Initiate a recognition.
+        startRecognition(module, handle, hwHandle);
+
+        // Stop the recognition.
+        stopRecognition(module, handle, hwHandle);
+
+        // Unload the model.
+        unloadModel(module, handle, hwHandle);
+        module.detach();
+    }
+
+    @Test
+    public void testRecognition() throws Exception {
+        initService(true);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+
+        // Load the model.
+        final int hwHandle = 7;
+        int handle = loadGenericModel(module, hwHandle);
+
+        // Initiate a recognition.
+        SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle);
+
+        // Signal a capture from the driver.
+        hwCallback.sendRecognitionEvent(hwHandle,
+                android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS);
+
+        ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+                RecognitionEvent.class);
+        verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+
+        // Validate the event.
+        validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.SUCCESS);
+
+        // Unload the model.
+        unloadModel(module, handle, hwHandle);
+        module.detach();
+    }
+
+    @Test
+    public void testPhraseRecognition() throws Exception {
+        initService(true);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+
+        // Load the model.
+        final int hwHandle = 7;
+        int handle = loadPhraseModel(module, hwHandle);
+
+        // Initiate a recognition.
+        SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle);
+
+        // Signal a capture from the driver.
+        hwCallback.sendPhraseRecognitionEvent(hwHandle,
+                android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS);
+
+        ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+                PhraseRecognitionEvent.class);
+        verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
+
+        // Validate the event.
+        validatePhraseRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.SUCCESS);
+
+        // Unload the model.
+        unloadModel(module, handle, hwHandle);
+        module.detach();
+    }
+
+    @Test
+    public void testForceRecognition() throws Exception {
+        if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw)) {
+            return;
+        }
+
+        android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver =
+                (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
+
+        initService(true);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+
+        // Load the model.
+        final int hwHandle = 17;
+        int handle = loadGenericModel(module, hwHandle);
+
+        // Initiate a recognition.
+        SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle);
+
+        // Force a trigger.
+        module.forceRecognitionEvent(handle);
+        verify(driver).getModelState(hwHandle);
+
+        // Signal a capture from the driver.
+        // '3' means 'forced', there's no constant for that in the HAL.
+        hwCallback.sendRecognitionEvent(hwHandle, 3);
+
+        ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+                RecognitionEvent.class);
+        verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+
+        // Validate the event.
+        validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.FORCED);
+
+        // Stop the recognition.
+        stopRecognition(module, handle, hwHandle);
+
+        // Unload the model.
+        unloadModel(module, handle, hwHandle);
+        module.detach();
+    }
+
+    @Test
+    public void testForcePhraseRecognition() throws Exception {
+        if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw)) {
+            return;
+        }
+
+        android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver =
+                (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
+
+        initService(true);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+
+        // Load the model.
+        final int hwHandle = 17;
+        int handle = loadPhraseModel(module, hwHandle);
+
+        // Initiate a recognition.
+        SoundTriggerHwCallback hwCallback = startRecognition(module, handle, hwHandle);
+
+        // Force a trigger.
+        module.forceRecognitionEvent(handle);
+        verify(driver).getModelState(hwHandle);
+
+        // Signal a capture from the driver.
+        // '3' means 'forced', there's no constant for that in the HAL.
+        hwCallback.sendPhraseRecognitionEvent(hwHandle, 3);
+
+        ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+                PhraseRecognitionEvent.class);
+        verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
+
+        // Validate the event.
+        validatePhraseRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.FORCED);
+
+        // Stop the recognition.
+        stopRecognition(module, handle, hwHandle);
+
+        // Unload the model.
+        unloadModel(module, handle, hwHandle);
+        module.detach();
+    }
+
+    @Test
+    public void testAbortRecognition() throws Exception {
+        // Make sure the HAL doesn't support concurrent capture.
+        initService(false);
+        mService.setExternalCaptureState(false);
+
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+        verify(callback).onRecognitionAvailabilityChange(true);
+
+        // Load the model.
+        final int hwHandle = 11;
+        int handle = loadGenericModel(module, hwHandle);
+
+        // Initiate a recognition.
+        startRecognition(module, handle, hwHandle);
+
+        // Abort.
+        mService.setExternalCaptureState(true);
+
+        ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+                RecognitionEvent.class);
+        verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+
+        // Validate the event.
+        assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
+
+        // Make sure we are notified of the lost availability.
+        verify(callback).onRecognitionAvailabilityChange(false);
+
+        // Attempt to start a new recognition - should get an abort event immediately, without
+        // involving the HAL.
+        clearInvocations(callback);
+        clearInvocations(mHalDriver);
+        module.startRecognition(handle, createRecognitionConfig());
+        verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+        assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
+        verifyNotStartRecognition();
+
+        // Now enable it and make sure we are notified.
+        mService.setExternalCaptureState(false);
+        verify(callback).onRecognitionAvailabilityChange(true);
+
+        // Unload the model.
+        unloadModel(module, handle, hwHandle);
+        module.detach();
+    }
+
+    @Test
+    public void testAbortPhraseRecognition() throws Exception {
+        // Make sure the HAL doesn't support concurrent capture.
+        initService(false);
+        mService.setExternalCaptureState(false);
+
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+        verify(callback).onRecognitionAvailabilityChange(true);
+
+        // Load the model.
+        final int hwHandle = 11;
+        int handle = loadPhraseModel(module, hwHandle);
+
+        // Initiate a recognition.
+        startRecognition(module, handle, hwHandle);
+
+        // Abort.
+        mService.setExternalCaptureState(true);
+
+        ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+                PhraseRecognitionEvent.class);
+        verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
+
+        // Validate the event.
+        assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
+
+        // Make sure we are notified of the lost availability.
+        verify(callback).onRecognitionAvailabilityChange(false);
+
+        // Attempt to start a new recognition - should get an abort event immediately, without
+        // involving the HAL.
+        clearInvocations(callback);
+        clearInvocations(mHalDriver);
+        module.startRecognition(handle, createRecognitionConfig());
+        verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
+        assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
+        verifyNotStartRecognition();
+
+        // Now enable it and make sure we are notified.
+        mService.setExternalCaptureState(false);
+        verify(callback).onRecognitionAvailabilityChange(true);
+
+        // Unload the model.
+        unloadModel(module, handle, hwHandle);
+        module.detach();
+    }
+
+    @Test
+    public void testNotAbortRecognitionConcurrent() throws Exception {
+        // Make sure the HAL supports concurrent capture.
+        initService(true);
+
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+        verify(callback).onRecognitionAvailabilityChange(true);
+        clearInvocations(callback);
+
+        // Load the model.
+        final int hwHandle = 13;
+        int handle = loadGenericModel(module, hwHandle);
+
+        // Initiate a recognition.
+        startRecognition(module, handle, hwHandle);
+
+        // Signal concurrent capture. Shouldn't abort.
+        mService.setExternalCaptureState(true);
+        verify(callback, never()).onRecognition(anyInt(), any());
+        verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean());
+
+        // Stop the recognition.
+        stopRecognition(module, handle, hwHandle);
+
+        // Initiating a new one should work fine.
+        clearInvocations(mHalDriver);
+        startRecognition(module, handle, hwHandle);
+        verify(callback, never()).onRecognition(anyInt(), any());
+        stopRecognition(module, handle, hwHandle);
+
+        // Unload the model.
+        module.unloadModel(handle);
+        module.detach();
+    }
+
+    @Test
+    public void testNotAbortPhraseRecognitionConcurrent() throws Exception {
+        // Make sure the HAL supports concurrent capture.
+        initService(true);
+
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+        verify(callback).onRecognitionAvailabilityChange(true);
+        clearInvocations(callback);
+
+        // Load the model.
+        final int hwHandle = 13;
+        int handle = loadPhraseModel(module, hwHandle);
+
+        // Initiate a recognition.
+        startRecognition(module, handle, hwHandle);
+
+        // Signal concurrent capture. Shouldn't abort.
+        mService.setExternalCaptureState(true);
+        verify(callback, never()).onPhraseRecognition(anyInt(), any());
+        verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean());
+
+        // Stop the recognition.
+        stopRecognition(module, handle, hwHandle);
+
+        // Initiating a new one should work fine.
+        clearInvocations(mHalDriver);
+        startRecognition(module, handle, hwHandle);
+        verify(callback, never()).onRecognition(anyInt(), any());
+        stopRecognition(module, handle, hwHandle);
+
+        // Unload the model.
+        module.unloadModel(handle);
+        module.detach();
+    }
+
+    @Test
+    public void testParameterSupported() throws Exception {
+        if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
+            return;
+        }
+
+        android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+        initService(false);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+        final int hwHandle = 12;
+        int modelHandle = loadGenericModel(module, hwHandle);
+
+        doAnswer((Answer<Void>) invocation -> {
+            android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
+                    resultCallback = invocation.getArgument(2);
+            android.hardware.soundtrigger.V2_3.ModelParameterRange range =
+                    new android.hardware.soundtrigger.V2_3.ModelParameterRange();
+            range.start = 23;
+            range.end = 45;
+            OptionalModelParameterRange optionalRange = new OptionalModelParameterRange();
+            optionalRange.range(range);
+            resultCallback.onValues(0, optionalRange);
+            return null;
+        }).when(driver).queryParameter(eq(hwHandle),
+                eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+
+        ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
+                ModelParameter.THRESHOLD_FACTOR);
+
+        verify(driver).queryParameter(eq(hwHandle),
+                eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+
+        assertEquals(23, range.minInclusive);
+        assertEquals(45, range.maxInclusive);
+    }
+
+    @Test
+    public void testParameterNotSupportedOld() throws Exception {
+        if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+            return;
+        }
+
+        initService(false);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+        final int hwHandle = 13;
+        int modelHandle = loadGenericModel(module, hwHandle);
+
+        ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
+                ModelParameter.THRESHOLD_FACTOR);
+
+        assertNull(range);
+    }
+
+    @Test
+    public void testParameterNotSupported() throws Exception {
+        if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
+            return;
+        }
+
+        android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+        initService(false);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+        final int hwHandle = 13;
+        int modelHandle = loadGenericModel(module, hwHandle);
+
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
+                    resultCallback = invocation.getArgument(2);
+            // This is the return of this method.
+            resultCallback.onValues(0, new OptionalModelParameterRange());
+            return null;
+        }).when(driver).queryParameter(eq(hwHandle),
+                eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+
+        ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
+                ModelParameter.THRESHOLD_FACTOR);
+
+        verify(driver).queryParameter(eq(hwHandle),
+                eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+
+        assertNull(range);
+    }
+
+    @Test
+    public void testGetParameter() throws Exception {
+        if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
+            return;
+        }
+
+        android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+        initService(false);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+        final int hwHandle = 14;
+        int modelHandle = loadGenericModel(module, hwHandle);
+
+        doAnswer(invocation -> {
+            android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getParameterCallback
+                    resultCallback = invocation.getArgument(2);
+            // This is the return of this method.
+            resultCallback.onValues(0, 234);
+            return null;
+        }).when(driver).getParameter(eq(hwHandle),
+                eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+
+        int value = module.getModelParameter(modelHandle, ModelParameter.THRESHOLD_FACTOR);
+
+        verify(driver).getParameter(eq(hwHandle),
+                eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+
+        assertEquals(234, value);
+    }
+
+    @Test
+    public void testSetParameter() throws Exception {
+        if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
+            return;
+        }
+
+        android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+                (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+        initService(false);
+        ISoundTriggerCallback callback = createCallbackMock();
+        ISoundTriggerModule module = mService.attach(0, callback);
+        final int hwHandle = 17;
+        int modelHandle = loadGenericModel(module, hwHandle);
+
+        when(driver.setParameter(hwHandle,
+                android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR,
+                456)).thenReturn(0);
+
+        module.setModelParameter(modelHandle, ModelParameter.THRESHOLD_FACTOR, 456);
+
+        verify(driver).setParameter(hwHandle,
+                android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR, 456);
+    }
+
+    private static class SoundTriggerHwCallback {
+        private final android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback mCallback;
+        private final int mCookie;
+
+        SoundTriggerHwCallback(android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback,
+                int cookie) {
+            mCallback = callback;
+            mCookie = cookie;
+        }
+
+        private void sendRecognitionEvent(int hwHandle, int status) throws RemoteException {
+            if (mCallback instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) {
+                ((android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) mCallback).recognitionCallback_2_1(
+                        createRecognitionEvent_2_1(hwHandle, status), mCookie);
+            } else {
+                mCallback.recognitionCallback(createRecognitionEvent_2_0(hwHandle, status),
+                        mCookie);
+            }
+        }
+
+        private void sendPhraseRecognitionEvent(int hwHandle, int status) throws RemoteException {
+            if (mCallback instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) {
+                ((android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) mCallback).phraseRecognitionCallback_2_1(
+                        createPhraseRecognitionEvent_2_1(hwHandle, status), mCookie);
+            } else {
+                mCallback.phraseRecognitionCallback(
+                        createPhraseRecognitionEvent_2_0(hwHandle, status), mCookie);
+            }
+        }
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index 0b4760d..a328568 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.spy;
 
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -271,6 +272,18 @@
     }
 
     @Test
+    public void testRankingScoreOverrides() {
+        NotificationComparator comp = new NotificationComparator(mContext);
+        NotificationRecord recordMinCallNonInterruptive = spy(mRecordMinCallNonInterruptive);
+        assertTrue(comp.compare(mRecordMinCall, recordMinCallNonInterruptive) < 0);
+
+        when(recordMinCallNonInterruptive.getRankingScore()).thenReturn(1f);
+        assertTrue(comp.compare(mRecordMinCall, recordMinCallNonInterruptive) > 0);
+        assertTrue(comp.compare(mRecordCheater, recordMinCallNonInterruptive) > 0);
+        assertTrue(comp.compare(mRecordColorizedCall, recordMinCallNonInterruptive) < 0);
+    }
+
+    @Test
     public void testMessaging() {
         NotificationComparator comp = new NotificationComparator(mContext);
         assertTrue(comp.isImportantMessaging(mRecordInlineReply));
diff --git a/services/usb/java/com/android/server/usb/UsbHandlerManager.java b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
index 1730d8f..f311274 100644
--- a/services/usb/java/com/android/server/usb/UsbHandlerManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
@@ -59,8 +60,9 @@
         if (uri != null && uri.length() > 0) {
             // display URI to user
             Intent dialogIntent = createDialogIntent();
-            dialogIntent.setClassName("com.android.systemui",
-                    "com.android.systemui.usb.UsbAccessoryUriActivity");
+            dialogIntent.setComponent(ComponentName.unflattenFromString(
+                    mContext.getResources().getString(
+                            com.android.internal.R.string.config_usbAccessoryUriActivity)));
             dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
             dialogIntent.putExtra("uri", uri);
             try {
@@ -84,8 +86,9 @@
             @Nullable UsbAccessory accessory) {
         Intent resolverIntent = createDialogIntent();
         // start UsbConfirmActivity if there is only one choice
-        resolverIntent.setClassName("com.android.systemui",
-                "com.android.systemui.usb.UsbConfirmActivity");
+        resolverIntent.setComponent(ComponentName.unflattenFromString(
+                mContext.getResources().getString(
+                        com.android.internal.R.string.config_usbConfirmActivity)));
         resolverIntent.putExtra("rinfo", rInfo);
         UserHandle user =
                 UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
@@ -115,8 +118,9 @@
     void selectUsbHandler(@NonNull ArrayList<ResolveInfo> matches,
             @NonNull UserHandle user, @NonNull Intent intent) {
         Intent resolverIntent = createDialogIntent();
-        resolverIntent.setClassName("com.android.systemui",
-                "com.android.systemui.usb.UsbResolverActivity");
+        resolverIntent.setComponent(ComponentName.unflattenFromString(
+                mContext.getResources().getString(
+                        com.android.internal.R.string.config_usbResolverActivity)));
         resolverIntent.putParcelableArrayListExtra("rlist", matches);
         resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
 
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 749258e..c3e2013 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -34,6 +34,7 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -225,8 +226,8 @@
 
             Intent intent = new Intent();
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            intent.setClassName("com.android.systemui",
-                    "com.android.systemui.usb.UsbContaminantActivity");
+            intent.setComponent(ComponentName.unflattenFromString(r.getString(
+                    com.android.internal.R.string.config_usbContaminantActivity)));
             intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(currentPortInfo.mUsbPort));
 
             PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
diff --git a/services/usb/java/com/android/server/usb/UsbSerialReader.java b/services/usb/java/com/android/server/usb/UsbSerialReader.java
index 86016bb..095e8e9 100644
--- a/services/usb/java/com/android/server/usb/UsbSerialReader.java
+++ b/services/usb/java/com/android/server/usb/UsbSerialReader.java
@@ -75,12 +75,14 @@
         if (uid != Process.SYSTEM_UID) {
             enforcePackageBelongsToUid(uid, packageName);
 
+            UserHandle user = Binder.getCallingUserHandle();
             int packageTargetSdkVersion;
             long token = Binder.clearCallingIdentity();
             try {
                 PackageInfo pkg;
                 try {
-                    pkg = mContext.getPackageManager().getPackageInfo(packageName, 0);
+                    pkg = mContext.getPackageManager()
+                            .getPackageInfoAsUser(packageName, 0, user.getIdentifier());
                 } catch (PackageManager.NameNotFoundException e) {
                     throw new RemoteException("package " + packageName + " cannot be found");
                 }
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index 58f5484..2a94393 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -515,8 +516,8 @@
         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.setComponent(ComponentName.unflattenFromString(userContext.getResources().getString(
+                com.android.internal.R.string.config_usbPermissionActivity)));
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
         try {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index c063279..0becaf2 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -695,6 +695,15 @@
     public static final String EVENT_CALL_HOLD_FAILED = "android.telecom.event.CALL_HOLD_FAILED";
 
     /**
+     * Connection event used to inform Telecom when a switch operation on a call has failed.
+     * <p>
+     * Sent via {@link #sendConnectionEvent(String, Bundle)}.  The {@link Bundle} parameter is
+     * expected to be null when this connection event is used.
+     */
+    public static final String EVENT_CALL_SWITCH_FAILED =
+            "android.telecom.event.CALL_SWITCH_FAILED";
+
+    /**
      * Connection event used to inform {@link InCallService}s when the process of merging a
      * Connection into a conference has begun.
      * <p>
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 001888c..7381acb 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -419,12 +419,33 @@
             KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
 
     /**
-     * Override the device's configuration for the ImsService to use for this SIM card.
+     * The package name containing the ImsService that will be bound to the telephony framework to
+     * support both IMS MMTEL and RCS feature functionality instead of the device default
+     * ImsService for this subscription.
+     * @deprecated Use {@link #KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING} and
+     * {@link #KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING} instead to configure these values
+     * separately. If any of those values are not empty, they will override this value.
      */
     public static final String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING =
             "config_ims_package_override_string";
 
     /**
+     * The package name containing the ImsService that will be bound to the telephony framework to
+     * support IMS MMTEL feature functionality instead of the device default ImsService for this
+     * subscription.
+     */
+    public static final String KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING =
+            "config_ims_mmtel_package_override_string";
+
+    /**
+     * The package name containing the ImsService that will be bound to the telephony framework to
+     * support IMS RCS feature functionality instead of the device default ImsService for this
+     * subscription.
+     */
+    public static final String KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING =
+            "config_ims_rcs_package_override_string";
+
+    /**
      * Override the package that will manage {@link SubscriptionPlan}
      * information instead of the {@link CarrierService} that defines this
      * value.
@@ -1946,6 +1967,12 @@
             "allow_add_call_during_video_call";
 
     /**
+     * When false, indicates that holding a video call is disabled
+     */
+    public static final String KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL =
+            "allow_holding_video_call";
+
+    /**
      * When true, indicates that the HD audio icon in the in-call screen should not be shown for
      * VoWifi calls.
      * @hide
@@ -2176,7 +2203,7 @@
      * the start of the next month.
      * <p>
      * This setting may be still overridden by explicit user choice. By default,
-     * the platform value will be used.
+     * {@link #DATA_CYCLE_USE_PLATFORM_DEFAULT} will be used.
      */
     public static final String KEY_MONTHLY_DATA_CYCLE_DAY_INT =
             "monthly_data_cycle_day_int";
@@ -2185,10 +2212,7 @@
      * When {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, {@link #KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG},
      * or {@link #KEY_DATA_WARNING_THRESHOLD_BYTES_LONG} are set to this value, the platform default
      * value will be used for that key.
-     *
-     * @hide
      */
-    @Deprecated
     public static final int DATA_CYCLE_USE_PLATFORM_DEFAULT = -1;
 
     /**
@@ -2212,8 +2236,8 @@
      * If the value is set to {@link #DATA_CYCLE_THRESHOLD_DISABLED}, the data usage warning will
      * be disabled.
      * <p>
-     * This setting may be overridden by explicit user choice. By default, the platform value
-     * will be used.
+     * This setting may be overridden by explicit user choice. By default,
+     * {@link #DATA_CYCLE_USE_PLATFORM_DEFAULT} will be used.
      */
     public static final String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG =
             "data_warning_threshold_bytes_long";
@@ -2221,8 +2245,7 @@
     /**
      * Controls if the device should automatically notify the user as they reach
      * their cellular data warning. When set to {@code false} the carrier is
-     * expected to have implemented their own notification mechanism.
-     * @hide
+     * expected to have implemented their own notification mechanism. {@code true} by default.
      */
     public static final String KEY_DATA_WARNING_NOTIFICATION_BOOL =
             "data_warning_notification_bool";
@@ -2244,8 +2267,8 @@
      * phone. If the value is set to {@link #DATA_CYCLE_THRESHOLD_DISABLED}, the data limit will be
      * disabled.
      * <p>
-     * This setting may be overridden by explicit user choice. By default, the platform value
-     * will be used.
+     * This setting may be overridden by explicit user choice. By default,
+     * {@link #DATA_CYCLE_USE_PLATFORM_DEFAULT} will be used.
      */
     public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG =
             "data_limit_threshold_bytes_long";
@@ -2253,8 +2276,7 @@
     /**
      * Controls if the device should automatically notify the user as they reach
      * their cellular data limit. When set to {@code false} the carrier is
-     * expected to have implemented their own notification mechanism.
-     * @hide
+     * expected to have implemented their own notification mechanism. {@code true} by default.
      */
     public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL =
             "data_limit_notification_bool";
@@ -2262,8 +2284,7 @@
     /**
      * Controls if the device should automatically notify the user when rapid
      * cellular data usage is observed. When set to {@code false} the carrier is
-     * expected to have implemented their own notification mechanism.
-     * @hide
+     * expected to have implemented their own notification mechanism.  {@code true} by default.
      */
     public static final String KEY_DATA_RAPID_NOTIFICATION_BOOL =
             "data_rapid_notification_bool";
@@ -3486,6 +3507,8 @@
         sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putString(KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
+        sDefaults.putString(KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING, null);
+        sDefaults.putString(KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING, null);
         sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_DIAL_STRING_REPLACE_STRING_ARRAY, null);
@@ -3629,6 +3652,7 @@
         sDefaults.putBoolean(KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL, false);
         sDefaults.putBoolean(KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL, true);
         sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_WIFI_CALLS_CAN_BE_HD_AUDIO, true);
         sDefaults.putBoolean(KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO, true);
         sDefaults.putBoolean(KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO, false);
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 02d8be3..39af34c 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -16,7 +16,10 @@
 
 package android.telephony.ims;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.telephony.SubscriptionManager;
 
@@ -25,12 +28,15 @@
  *
  * @hide
  */
+@SystemApi
+@TestApi
 @SystemService(Context.TELEPHONY_IMS_SERVICE)
 public class ImsManager {
 
     private Context mContext;
 
-    public ImsManager(Context context) {
+    /** @hide */
+    public ImsManager(@NonNull Context context) {
         mContext = context;
     }
 
@@ -41,6 +47,7 @@
      * @throws IllegalArgumentException if the subscription is invalid.
      * @return a ImsRcsManager instance with the specific subscription ID.
      */
+    @NonNull
     public ImsRcsManager getImsRcsManager(int subscriptionId) {
         if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
             throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
@@ -56,6 +63,7 @@
      * @throws IllegalArgumentException if the subscription is invalid.
      * @return a ImsMmTelManager instance with the specific subscription ID.
      */
+    @NonNull
     public ImsMmTelManager getImsMmTelManager(int subscriptionId) {
         if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
             throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java
index 0ceb103..c8b8ffb 100644
--- a/telephony/java/android/telephony/NetworkScanRequest.java
+++ b/telephony/java/android/telephony/NetworkScanRequest.java
@@ -222,9 +222,13 @@
     private NetworkScanRequest(Parcel in) {
         mScanType = in.readInt();
         Parcelable[] tempSpecifiers = in.readParcelableArray(Object.class.getClassLoader());
-        mSpecifiers = new RadioAccessSpecifier[tempSpecifiers.length];
-        for (int i = 0; i < tempSpecifiers.length; i++) {
-            mSpecifiers[i] = (RadioAccessSpecifier) tempSpecifiers[i];
+        if (tempSpecifiers != null) {
+            mSpecifiers = new RadioAccessSpecifier[tempSpecifiers.length];
+            for (int i = 0; i < tempSpecifiers.length; i++) {
+                mSpecifiers[i] = (RadioAccessSpecifier) tempSpecifiers[i];
+            }
+        } else {
+            mSpecifiers = null;
         }
         mSearchPeriodicity = in.readInt();
         mMaxSearchTime = in.readInt();
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 257d634..78ad5c5 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -19,8 +19,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.net.LinkProperties;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.Annotation.ApnType;
@@ -29,6 +31,8 @@
 import android.telephony.Annotation.NetworkType;
 import android.telephony.data.ApnSetting;
 
+import dalvik.system.VMRuntime;
+
 import java.util.Objects;
 
 
@@ -46,35 +50,62 @@
  *   <li>Data connection fail cause.
  * </ul>
  *
- * @hide
  */
-@SystemApi
 public final class PreciseDataConnectionState implements Parcelable {
 
     private @DataState int mState = TelephonyManager.DATA_UNKNOWN;
     private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
     private @DataFailureCause int mFailCause = DataFailCause.NONE;
-    private @ApnType int mAPNTypes = ApnSetting.TYPE_NONE;
-    private String mAPN = "";
+    private @ApnType int mApnTypes = ApnSetting.TYPE_NONE;
+    private String mApn = "";
     private LinkProperties mLinkProperties = null;
+    private ApnSetting mApnSetting = null;
 
     /**
      * Constructor
      *
+     * @deprecated this constructor has been superseded and should not be used.
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @Deprecated
+    @UnsupportedAppUsage // (maxTargetSdk = Build.VERSION_CODES.Q)
+    // FIXME: figure out how to remove the UnsupportedAppUsage and delete this constructor
     public PreciseDataConnectionState(@DataState int state,
                                       @NetworkType int networkType,
-                                      @ApnType int apnTypes, String apn,
-                                      LinkProperties linkProperties,
+                                      @ApnType int apnTypes, @NonNull String apn,
+                                      @Nullable LinkProperties linkProperties,
                                       @DataFailureCause int failCause) {
+        this(state, networkType, apnTypes, apn, linkProperties, failCause, null);
+    }
+
+
+    /**
+     * Constructor
+     *
+     * @param state the state of the data connection
+     * @param networkType the access network that is/would carry this data connection
+     * @param apnTypes the APN types that this data connection carries
+     * @param apnSetting if there is a valid APN for this Data Connection, then the APN Settings;
+     *        if there is no valid APN setting for the specific type, then this will be null
+     * @param linkProperties if the data connection is connected, the properties of the connection
+     * @param failCause in case a procedure related to this data connection fails, a non-zero error
+     *        code indicating the cause of the failure.
+     * @hide
+     */
+    public PreciseDataConnectionState(@DataState int state,
+                                      @NetworkType int networkType,
+                                      @ApnType int apnTypes, @NonNull String apn,
+                                      @Nullable LinkProperties linkProperties,
+                                      @DataFailureCause int failCause,
+                                      @Nullable ApnSetting apnSetting) {
         mState = state;
         mNetworkType = networkType;
-        mAPNTypes = apnTypes;
-        mAPN = apn;
+        mApnTypes = apnTypes;
+        mApn = apn;
         mLinkProperties = linkProperties;
         mFailCause = failCause;
+        mApnSetting = apnSetting;
     }
 
     /**
@@ -93,76 +124,160 @@
     private PreciseDataConnectionState(Parcel in) {
         mState = in.readInt();
         mNetworkType = in.readInt();
-        mAPNTypes = in.readInt();
-        mAPN = in.readString();
-        mLinkProperties = (LinkProperties)in.readParcelable(null);
+        mApnTypes = in.readInt();
+        mApn = in.readString();
+        mLinkProperties = (LinkProperties) in.readParcelable(null);
         mFailCause = in.readInt();
+        mApnSetting = (ApnSetting) in.readParcelable(null);
     }
 
     /**
      * Returns the state of data connection that supported the apn types returned by
      * {@link #getDataConnectionApnTypeBitMask()}
+     *
+     * @deprecated use {@link #getState()}
+     * @hide
      */
+    @Deprecated
+    @SystemApi
     public @DataState int getDataConnectionState() {
+        if (mState == TelephonyManager.DATA_DISCONNECTING
+                && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) {
+            return TelephonyManager.DATA_CONNECTED;
+        }
+
+        return mState;
+    }
+
+    /**
+     * Returns the high-level state of this data connection.
+     */
+    public @DataState int getState() {
         return mState;
     }
 
     /**
      * Returns the network type associated with this data connection.
+     *
+     * @deprecated use {@link getNetworkType()}
      * @hide
      */
+    @Deprecated
+    @SystemApi
     public @NetworkType int getDataConnectionNetworkType() {
         return mNetworkType;
     }
 
     /**
-     * Returns the data connection APN types supported by this connection and triggers
-     * {@link PreciseDataConnectionState} change.
+     * Returns the network type associated with this data connection.
+     *
+     * Return the current/latest (radio) bearer technology that carries this data connection.
+     * For a variety of reasons, the network type can change during the life of the data
+     * connection, and this information is not reliable unless the physical link is currently
+     * active; (there is currently no mechanism to know whether the physical link is active at
+     * any given moment). Thus, this value is generally correct but may not be relied-upon to
+     * represent the status of the radio bearer at any given moment.
      */
-    public @ApnType int getDataConnectionApnTypeBitMask() {
-        return mAPNTypes;
+    public @NetworkType int getNetworkType() {
+        return mNetworkType;
     }
 
     /**
-     * Returns APN {@link ApnSetting} of this data connection.
+     * Returns the APN types mapped to this data connection.
+     *
+     * @deprecated use {@link #getApnSetting()}
+     * @hide
      */
-    @Nullable
+    @Deprecated
+    @SystemApi
+    public @ApnType int getDataConnectionApnTypeBitMask() {
+        return mApnTypes;
+    }
+
+    /**
+     * Returns APN of this data connection.
+     *
+     * @deprecated use {@link #getApnSetting()}
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @Deprecated
     public String getDataConnectionApn() {
-        return mAPN;
+        return mApn;
     }
 
     /**
      * Get the properties of the network link {@link LinkProperties}.
+     *
+     * @deprecated use {@link #getLinkProperties()}
      * @hide
      */
-    @UnsupportedAppUsage
+    @Deprecated
+    @SystemApi
+    @Nullable
     public LinkProperties getDataConnectionLinkProperties() {
         return mLinkProperties;
     }
 
     /**
-     * Returns data connection fail cause, in case there was a failure.
+     * Get the properties of the network link {@link LinkProperties}.
      */
-    public @Annotation.DataFailureCause int getDataConnectionFailCause() {
+    @Nullable
+    public LinkProperties getLinkProperties() {
+        return mLinkProperties;
+    }
+
+    /**
+     * Returns the cause code generated by the most recent state change.
+     *
+     * @deprecated use {@link #getLastCauseCode()}
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    public int getDataConnectionFailCause() {
         return mFailCause;
     }
 
+    /**
+     * Returns the cause code generated by the most recent state change.
+     *
+     * Return the cause code for the most recent change in {@link #getState}. In the event of an
+     * error, this cause code will be non-zero.
+     */
+    // FIXME(b144774287): some of these cause codes should have a prescribed meaning.
+    public int getLastCauseCode() {
+        return mFailCause;
+    }
+
+    /**
+     * Return the APN Settings for this data connection.
+     *
+     * Returns the ApnSetting that was used to configure this data connection.
+     */
+    // FIXME: This shouldn't be nullable; update once the ApnSetting is supplied correctly
+    @Nullable ApnSetting getApnSetting() {
+        return mApnSetting;
+    }
+
     @Override
     public int describeContents() {
         return 0;
     }
 
     @Override
-    public void writeToParcel(Parcel out, int flags) {
+    public void writeToParcel(@NonNull Parcel out, int flags) {
         out.writeInt(mState);
         out.writeInt(mNetworkType);
-        out.writeInt(mAPNTypes);
-        out.writeString(mAPN);
+        out.writeInt(mApnTypes);
+        out.writeString(mApn);
         out.writeParcelable(mLinkProperties, flags);
         out.writeInt(mFailCause);
+        out.writeParcelable(mApnSetting, flags);
     }
 
-    public static final @android.annotation.NonNull Parcelable.Creator<PreciseDataConnectionState> CREATOR
+    public static final @NonNull Parcelable.Creator<PreciseDataConnectionState> CREATOR
             = new Parcelable.Creator<PreciseDataConnectionState>() {
 
         public PreciseDataConnectionState createFromParcel(Parcel in) {
@@ -176,8 +291,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mState, mNetworkType, mAPNTypes, mAPN, mLinkProperties,
-                mFailCause);
+        return Objects.hash(mState, mNetworkType, mApnTypes, mApn, mLinkProperties,
+                mFailCause, mApnSetting);
     }
 
     @Override
@@ -188,11 +303,12 @@
         }
 
         PreciseDataConnectionState other = (PreciseDataConnectionState) obj;
-        return Objects.equals(mAPN, other.mAPN) && mAPNTypes == other.mAPNTypes
+        return Objects.equals(mApn, other.mApn) && mApnTypes == other.mApnTypes
                 && mFailCause == other.mFailCause
                 && Objects.equals(mLinkProperties, other.mLinkProperties)
                 && mNetworkType == other.mNetworkType
-                && mState == other.mState;
+                && mState == other.mState
+                && Objects.equals(mApnSetting, other.mApnSetting);
     }
 
     @NonNull
@@ -202,10 +318,11 @@
 
         sb.append("Data Connection state: " + mState);
         sb.append(", Network type: " + mNetworkType);
-        sb.append(", APN types: " + ApnSetting.getApnTypesStringFromBitmask(mAPNTypes));
-        sb.append(", APN: " + mAPN);
+        sb.append(", APN types: " + ApnSetting.getApnTypesStringFromBitmask(mApnTypes));
+        sb.append(", APN: " + mApn);
         sb.append(", Link properties: " + mLinkProperties);
         sb.append(", Fail cause: " + DataFailCause.toString(mFailCause));
+        sb.append(", Apn Setting: " + mApnSetting);
 
         return sb.toString();
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4fba625..78c5e43 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -38,6 +38,9 @@
 import android.annotation.WorkerThread;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -142,6 +145,14 @@
     private static final String TAG = "TelephonyManager";
 
     /**
+     * To expand the error codes for {@link TelephonyManager#updateAvailableNetworks} and
+     * {@link TelephonyManager#setPreferredOpportunisticDataSubscription}.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    private static final long CALLBACK_ON_MORE_ERROR_CODE_CHANGE = 130595455L;
+
+    /**
      * The key to use when placing the result of {@link #requestModemActivityInfo(ResultReceiver)}
      * into the ResultReceiver Bundle.
      * @hide
@@ -790,18 +801,6 @@
     public static final String EXTRA_DATA_APN = PhoneConstants.DATA_APN_KEY;
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for an String representation of the data interface.
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getParcelableExtra(String name)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_LINK_PROPERTIES_KEY = PhoneConstants.DATA_LINK_PROPERTIES_KEY;
-
-    /**
      * Broadcast intent action for letting the default dialer to know to show voicemail
      * notification.
      *
@@ -2864,6 +2863,24 @@
     //
     //
 
+    /** @hide */
+    @IntDef(prefix = {"SIM_STATE_"},
+            value = {
+                    SIM_STATE_UNKNOWN,
+                    SIM_STATE_ABSENT,
+                    SIM_STATE_PIN_REQUIRED,
+                    SIM_STATE_PUK_REQUIRED,
+                    SIM_STATE_NETWORK_LOCKED,
+                    SIM_STATE_READY,
+                    SIM_STATE_NOT_READY,
+                    SIM_STATE_PERM_DISABLED,
+                    SIM_STATE_CARD_IO_ERROR,
+                    SIM_STATE_CARD_RESTRICTED,
+                    SIM_STATE_LOADED,
+                    SIM_STATE_PRESENT,
+            })
+    public @interface SimState {}
+
     /**
      * SIM card state: Unknown. Signifies that the SIM is in transition
      * between states. For example, when the user inputs the SIM pin
@@ -3069,7 +3086,7 @@
      * @see #SIM_STATE_CARD_IO_ERROR
      * @see #SIM_STATE_CARD_RESTRICTED
      */
-    public int getSimState() {
+    public @SimState int getSimState() {
         int simState = getSimStateIncludingLoaded();
         if (simState == SIM_STATE_LOADED) {
             simState = SIM_STATE_READY;
@@ -3077,7 +3094,7 @@
         return simState;
     }
 
-    private int getSimStateIncludingLoaded() {
+    private @SimState int getSimStateIncludingLoaded() {
         int slotIndex = getSlotIndex();
         // slotIndex may be invalid due to sim being absent. In that case query all slots to get
         // sim state
@@ -3111,7 +3128,7 @@
      * @hide
      */
     @SystemApi
-    public int getSimCardState() {
+    public @SimState int getSimCardState() {
         int simState = getSimState();
         return getSimCardStateFromSimState(simState);
     }
@@ -3131,7 +3148,7 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public int getSimCardState(int physicalSlotIndex) {
+    public @SimState int getSimCardState(int physicalSlotIndex) {
         int simState = getSimState(getLogicalSlotIndex(physicalSlotIndex));
         return getSimCardStateFromSimState(simState);
     }
@@ -3141,7 +3158,7 @@
      * @param simState
      * @return SIM card state
      */
-    private int getSimCardStateFromSimState(int simState) {
+    private @SimState int getSimCardStateFromSimState(int simState) {
         switch (simState) {
             case SIM_STATE_UNKNOWN:
             case SIM_STATE_ABSENT:
@@ -3181,7 +3198,7 @@
      * @hide
      */
     @SystemApi
-    public int getSimApplicationState() {
+    public @SimState int getSimApplicationState() {
         int simState = getSimStateIncludingLoaded();
         return getSimApplicationStateFromSimState(simState);
     }
@@ -3204,7 +3221,7 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public int getSimApplicationState(int physicalSlotIndex) {
+    public @SimState int getSimApplicationState(int physicalSlotIndex) {
         int simState =
                 SubscriptionManager.getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex));
         return getSimApplicationStateFromSimState(simState);
@@ -3215,7 +3232,7 @@
      * @param simState
      * @return SIM application state
      */
-    private int getSimApplicationStateFromSimState(int simState) {
+    private @SimState int getSimApplicationStateFromSimState(int simState) {
         switch (simState) {
             case SIM_STATE_UNKNOWN:
             case SIM_STATE_ABSENT:
@@ -3272,7 +3289,7 @@
      * @see #SIM_STATE_CARD_IO_ERROR
      * @see #SIM_STATE_CARD_RESTRICTED
      */
-    public int getSimState(int slotIndex) {
+    public @SimState int getSimState(int slotIndex) {
         int simState = SubscriptionManager.getSimStateForSlotIndex(slotIndex);
         if (simState == SIM_STATE_LOADED) {
             simState = SIM_STATE_READY;
@@ -5100,6 +5117,7 @@
             DATA_CONNECTING,
             DATA_CONNECTED,
             DATA_SUSPENDED,
+            DATA_DISCONNECTING,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DataState{}
@@ -5116,6 +5134,12 @@
      * traffic is temporarily unavailable. For example, in a 2G network,
      * data activity may be suspended when a voice call arrives. */
     public static final int DATA_SUSPENDED      = 3;
+    /**
+     * Data connection state: Disconnecting.
+     *
+     * IP traffic may be available but will cease working imminently.
+     */
+    public static final int DATA_DISCONNECTING = 4;
 
     /**
      * Returns a constant indicating the current data connection state
@@ -5125,14 +5149,21 @@
      * @see #DATA_CONNECTING
      * @see #DATA_CONNECTED
      * @see #DATA_SUSPENDED
+     * @see #DATA_DISCONNECTING
      */
     public int getDataState() {
         try {
             ITelephony telephony = getITelephony();
             if (telephony == null)
                 return DATA_DISCONNECTED;
-            return telephony.getDataStateForSubId(
+            int state = telephony.getDataStateForSubId(
                     getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
+            if (state == TelephonyManager.DATA_DISCONNECTING
+                    && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) {
+                return TelephonyManager.DATA_CONNECTED;
+            }
+
+            return state;
         } catch (RemoteException ex) {
             // the phone process is restarting.
             return DATA_DISCONNECTED;
@@ -5153,6 +5184,7 @@
             case DATA_CONNECTING: return "CONNECTING";
             case DATA_CONNECTED: return "CONNECTED";
             case DATA_SUSPENDED: return "SUSPENDED";
+            case DATA_DISCONNECTING: return "DISCONNECTING";
         }
         return "UNKNOWN(" + state + ")";
     }
@@ -6804,7 +6836,8 @@
      * If the list is longer than the size of EFfplmn, then the file will be written from the
      * beginning of the list up to the file size.
      *
-     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * <p>Requires Permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE
+     * MODIFY_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param fplmns a list of PLMNs to be forbidden.
@@ -6818,7 +6851,7 @@
     public int setForbiddenPlmns(@NonNull List<String> fplmns) {
         try {
             ITelephony telephony = getITelephony();
-            if (telephony == null) return 0;
+            if (telephony == null) return -1;
             return telephony.setForbiddenPlmns(
                     getSubId(), APPTYPE_USIM, fplmns, getOpPackageName(), getFeatureId());
         } catch (RemoteException ex) {
@@ -6827,7 +6860,7 @@
             // This could happen before phone starts
             Rlog.e(TAG, "setForbiddenPlmns NullPointerException: " + ex.getMessage());
         }
-        return 0;
+        return -1;
     }
 
     /**
@@ -10266,19 +10299,25 @@
     }
 
     /**
-     * Action set from carrier signalling broadcast receivers to enable/disable radio
-     * Permissions {@link android.Manifest.permission.MODIFY_PHONE_STATE} is required.
-     * @param subId the subscription ID that this action applies to.
+     * Carrier action to enable or disable the radio.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
      * @param enabled control enable or disable radio.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void carrierActionSetRadioEnabled(int subId, boolean enabled) {
+    public void setRadioEnabled(boolean enabled) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                service.carrierActionSetRadioEnabled(subId, enabled);
+                service.carrierActionSetRadioEnabled(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enabled);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#carrierActionSetRadioEnabled", e);
@@ -10286,20 +10325,25 @@
     }
 
     /**
-     * Action set from carrier signalling broadcast receivers to start/stop reporting default
-     * network available events
-     * Permissions {@link android.Manifest.permission.MODIFY_PHONE_STATE} is required.
-     * @param subId the subscription ID that this action applies to.
+     * Carrier action to start or stop reporting default network available events.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
      * @param report control start/stop reporting network status.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void carrierActionReportDefaultNetworkStatus(int subId, boolean report) {
+    public void reportDefaultNetworkStatus(boolean report) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                service.carrierActionReportDefaultNetworkStatus(subId, report);
+                service.carrierActionReportDefaultNetworkStatus(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), report);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#carrierActionReportDefaultNetworkStatus", e);
@@ -10307,18 +10351,24 @@
     }
 
     /**
-     * Action set from carrier signalling broadcast receivers to reset all carrier actions
-     * Permissions {@link android.Manifest.permission.MODIFY_PHONE_STATE} is required.
-     * @param subId the subscription ID that this action applies to.
+     * Reset all carrier actions previously set by {@link #setRadioEnabled},
+     * {@link #reportDefaultNetworkStatus} and {@link #setCarrierDataEnabled}.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void carrierActionResetAll(int subId) {
+    public void resetAllCarrierActions() {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                service.carrierActionResetAll(subId);
+                service.carrierActionResetAll(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#carrierActionResetAll", e);
@@ -11331,7 +11381,11 @@
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     executor.execute(() -> {
-                        callback.accept(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
+                        if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                            callback.accept(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
+                        } else {
+                            callback.accept(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
+                        }
                     });
                 } finally {
                     Binder.restoreCallingIdentity(identity);
@@ -11428,7 +11482,11 @@
                     final long identity = Binder.clearCallingIdentity();
                     try {
                         executor.execute(() -> {
-                            callback.accept(UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION);
+                            if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
+                                callback.accept(UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION);
+                            } else {
+                                callback.accept(UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE);
+                            }
                         });
                     } finally {
                         Binder.restoreCallingIdentity(identity);
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index b37d7c7..d3fb37f 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -20,6 +20,8 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.os.Binder;
 import android.os.IBinder;
@@ -30,6 +32,7 @@
 import android.telephony.ims.aidl.IImsRcsController;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 
 import java.util.concurrent.Executor;
@@ -42,6 +45,8 @@
  * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this manager.
  * @hide
  */
+@SystemApi
+@TestApi
 public class ImsRcsManager implements RegistrationManager {
     private static final String TAG = "ImsRcsManager";
 
@@ -105,7 +110,7 @@
          *
          * @param capabilities The new availability of the capabilities.
          */
-        public void onAvailabilityChanged(RcsFeature.RcsImsCapabilities capabilities) {
+        public void onAvailabilityChanged(@NonNull RcsFeature.RcsImsCapabilities capabilities) {
         }
 
         /**@hide*/
@@ -142,6 +147,7 @@
 
     /**{@inheritDoc}*/
     @Override
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void registerImsRegistrationCallback(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull RegistrationCallback c)
@@ -159,6 +165,7 @@
 
     /**{@inheritDoc}*/
     @Override
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void unregisterImsRegistrationCallback(
             @NonNull RegistrationManager.RegistrationCallback c) {
         if (c == null) {
@@ -170,6 +177,7 @@
 
     /**{@inheritDoc}*/
     @Override
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
             @NonNull @ImsRegistrationState Consumer<Integer> stateCallback) {
         if (stateCallback == null) {
@@ -184,6 +192,7 @@
 
     /**{@inheritDoc}*/
     @Override
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
             @NonNull @AccessNetworkConstants.TransportType
                     Consumer<Integer> transportTypeCallback) {
@@ -219,7 +228,7 @@
      * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public void registerRcsAvailabilityCallback(@CallbackExecutor Executor executor,
+    public void registerRcsAvailabilityCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull AvailabilityCallback c) throws ImsException {
         if (c == null) {
             throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
@@ -231,13 +240,13 @@
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
             Log.e(TAG, "Register availability callback: IImsRcsController is null");
-            throw new ImsException("Can not find remote IMS service",
+            throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
 
         c.setExecutor(executor);
         try {
-            imsRcsController.registerRcsAvailabilityCallback(c.getBinder());
+            imsRcsController.registerRcsAvailabilityCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e);
             throw new ImsException("Remote IMS Service is not available",
@@ -267,12 +276,12 @@
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
             Log.e(TAG, "Unregister availability callback: IImsRcsController is null");
-            throw new ImsException("Can not find remote IMS service",
+            throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
 
         try {
-            imsRcsController.unregisterRcsAvailabilityCallback(c.getBinder());
+            imsRcsController.unregisterRcsAvailabilityCallback(mSubId, c.getBinder());
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e);
             throw new ImsException("Remote IMS Service is not available",
@@ -287,6 +296,9 @@
      * RCS capabilities provided over-the-top by applications.
      *
      * @param capability The RCS capability to query.
+     * @param radioTech The radio tech that this capability failed for, defined as
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
+     * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
      * @return true if the RCS capability is capable for this subscription, false otherwise. This
      * does not necessarily mean that we are registered for IMS and the capability is available, but
      * rather the subscription is capable of this service over IMS.
@@ -297,17 +309,17 @@
      * See {@link ImsException#getCode()} for more information on the error codes.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability)
-            throws ImsException {
+    public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
             Log.e(TAG, "isCapable: IImsRcsController is null");
-            throw new ImsException("Can not find remote IMS service",
+            throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
 
         try {
-            return imsRcsController.isCapable(mSubId, capability);
+            return imsRcsController.isCapable(mSubId, capability, radioTech);
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling IImsRcsController#isCapable", e);
             throw new ImsException("Remote IMS Service is not available",
@@ -336,7 +348,7 @@
         IImsRcsController imsRcsController = getIImsRcsController();
         if (imsRcsController == null) {
             Log.e(TAG, "isAvailable: IImsRcsController is null");
-            throw new ImsException("Can not find remote IMS service",
+            throw new ImsException("Cannot find remote IMS service",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
 
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index b379bd0..e81bac0 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -26,9 +26,9 @@
  * {@hide}
  */
 interface IImsRcsController {
-    void registerRcsAvailabilityCallback(IImsCapabilityCallback c);
-    void unregisterRcsAvailabilityCallback(IImsCapabilityCallback c);
-    boolean isCapable(int subId, int capability);
+    void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
+    void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
+    boolean isCapable(int subId, int capability, int radioTech);
     boolean isAvailable(int subId, int capability);
 
     // ImsUceAdapter specific
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 0eaf8dc..884a0bc 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -193,7 +193,6 @@
      * of the capability and notify the capability status as true using
      * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
      * framework that the capability is available for usage.
-     * @hide
      */
     public static class RcsImsCapabilities extends Capabilities {
         /** @hide*/
@@ -207,7 +206,6 @@
 
         /**
          * Undefined capability type for initialization
-         * @hide
          */
         public static final int CAPABILITY_TYPE_NONE = 0;
 
@@ -215,7 +213,6 @@
          * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the
          * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS.
          * If not set, this RcsFeature should not service capability requests.
-         * @hide
          */
         public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0;
 
@@ -224,33 +221,27 @@
          * framework. If set, the RcsFeature should support capability exchange using a presence
          * server. If not set, this RcsFeature should not publish capabilities or service capability
          * requests using presence.
-         * @hide
          */
         public static final int CAPABILITY_TYPE_PRESENCE_UCE =  1 << 1;
 
-        /**@hide*/
         public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) {
             super(capabilities);
         }
 
-        /**@hide*/
         private RcsImsCapabilities(Capabilities c) {
             super(c.getMask());
         }
 
-        /**@hide*/
         @Override
         public void addCapabilities(@RcsImsCapabilityFlag int capabilities) {
             super.addCapabilities(capabilities);
         }
 
-        /**@hide*/
         @Override
         public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) {
             super.removeCapabilities(capabilities);
         }
 
-        /**@hide*/
         @Override
         public boolean isCapable(@RcsImsCapabilityFlag int capabilities) {
             return super.isCapable(capabilities);
@@ -295,10 +286,9 @@
      * set, the {@link RcsFeature} has brought up the capability and is ready for framework
      * requests. To change the status of the capabilities
      * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
-     * @hide
      */
     @Override
-    public final RcsImsCapabilities queryCapabilityStatus() {
+    public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
         return new RcsImsCapabilities(super.queryCapabilityStatus());
     }
 
@@ -306,7 +296,6 @@
      * Notify the framework that the capabilities status has changed. If a capability is enabled,
      * this signals to the framework that the capability has been initialized and is ready.
      * Call {@link #queryCapabilityStatus()} to return the current capability status.
-     * @hide
      */
     public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
         if (c == null) {
@@ -321,7 +310,6 @@
      * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to
      * enable or disable capability A, this method should return the correct configuration for
      * capability A afterwards (until it has changed).
-     * @hide
      */
     public boolean queryCapabilityConfiguration(
             @RcsImsCapabilities.RcsImsCapabilityFlag int capability,
@@ -343,11 +331,10 @@
      * If for some reason one or more of these capabilities can not be enabled/disabled,
      * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should
      * be called for each capability change that resulted in an error.
-     * @hide
      */
     @Override
-    public void changeEnabledCapabilities(CapabilityChangeRequest request,
-            CapabilityCallbackProxy c) {
+    public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
+            @NonNull CapabilityCallbackProxy c) {
         // Base Implementation - Override to provide functionality
     }
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0ec54ec..97b24ae 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -888,12 +888,13 @@
     /**
     *  @return true if the ImsService to bind to for the slot id specified was set, false otherwise.
     */
-    boolean setImsService(int slotId, boolean isCarrierImsService, String packageName);
+    boolean setBoundImsServiceOverride(int slotIndex, boolean isCarrierService,
+            in int[] featureTypes, in String packageName);
 
     /**
     * @return the package name of the carrier/device ImsService associated with this slot.
     */
-    String getImsService(int slotId, boolean isCarrierImsService);
+    String getBoundImsServicePackage(int slotIndex, boolean isCarrierImsService, int featureType);
 
     /**
      * Get the MmTelFeature state attached to this subscription id.
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstantConversions.java b/telephony/java/com/android/internal/telephony/PhoneConstantConversions.java
index f7f0f29..8640acc 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstantConversions.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstantConversions.java
@@ -16,14 +16,10 @@
 
 package com.android.internal.telephony;
 
-import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
-import android.telephony.PreciseCallState;
 
 import com.android.internal.telephony.PhoneConstants;
 
-import java.util.List;
-
 public class PhoneConstantConversions {
     /**
      * Convert the {@link PhoneConstants.State} enum into the TelephonyManager.CALL_STATE_*
@@ -67,6 +63,8 @@
                 return TelephonyManager.DATA_CONNECTED;
             case SUSPENDED:
                 return TelephonyManager.DATA_SUSPENDED;
+            case DISCONNECTING:
+                return TelephonyManager.DATA_DISCONNECTING;
             default:
                 return TelephonyManager.DATA_DISCONNECTED;
         }
@@ -84,6 +82,8 @@
                 return PhoneConstants.DataState.CONNECTED;
             case TelephonyManager.DATA_SUSPENDED:
                 return PhoneConstants.DataState.SUSPENDED;
+            case TelephonyManager.DATA_DISCONNECTING:
+                return PhoneConstants.DataState.DISCONNECTING;
             default:
                 return PhoneConstants.DataState.DISCONNECTED;
         }
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index fde2c5a..fadb573 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -46,6 +46,7 @@
       * <ul>
       * <li>CONNECTED = IP traffic should be available</li>
       * <li>CONNECTING = Currently setting up data connection</li>
+      * <li>DISCONNECTING = IP temporarily available</li>
       * <li>DISCONNECTED = IP not available</li>
       * <li>SUSPENDED = connection is created but IP traffic is
       *                 temperately not available. i.e. voice call is in place
@@ -55,10 +56,15 @@
     @UnsupportedAppUsage(implicitMember =
             "values()[Lcom/android/internal/telephony/PhoneConstants$DataState;")
     public enum DataState {
-        @UnsupportedAppUsage CONNECTED,
-        @UnsupportedAppUsage CONNECTING,
-        @UnsupportedAppUsage DISCONNECTED,
-        @UnsupportedAppUsage SUSPENDED;
+        @UnsupportedAppUsage
+        CONNECTED,
+        @UnsupportedAppUsage
+        CONNECTING,
+        @UnsupportedAppUsage
+        DISCONNECTED,
+        @UnsupportedAppUsage
+        SUSPENDED,
+        DISCONNECTING;
     };
 
     public static final String STATE_KEY = "state";
@@ -91,20 +97,12 @@
 
     public static final String PHONE_NAME_KEY = "phoneName";
     public static final String DATA_NETWORK_TYPE_KEY = "networkType";
-    public static final String DATA_FAILURE_CAUSE_KEY = "failCause";
     public static final String DATA_APN_TYPE_KEY = "apnType";
     public static final String DATA_APN_KEY = "apn";
-    public static final String DATA_LINK_PROPERTIES_KEY = "linkProperties";
-    public static final String DATA_NETWORK_CAPABILITIES_KEY = "networkCapabilities";
 
-    public static final String DATA_IFACE_NAME_KEY = "iface";
-    public static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable";
-    public static final String DATA_NETWORK_ROAMING_KEY = "networkRoaming";
     public static final String PHONE_IN_ECM_STATE = "phoneinECMState";
     public static final String PHONE_IN_EMERGENCY_CALL = "phoneInEmergencyCall";
 
-    public static final String REASON_LINK_PROPERTIES_CHANGED = "linkPropertiesChanged";
-
     /**
      * Return codes for supplyPinReturnResult and
      * supplyPukReturnResult APIs
diff --git a/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java b/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java
index e323592..026677e 100644
--- a/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java
+++ b/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java
@@ -53,6 +53,8 @@
      */
     @Test
     public void testCreateStartDelete() throws Exception {
+        // Disable package verifier for ADB installs.
+        getDevice().executeShellCommand("settings put global verifier_verify_adb_installs 0");
         int iteration = 0;
         final long deadline = System.nanoTime() + TimeUnit.MINUTES.toNanos(TIME_LIMIT_MINUTES);
         while (System.nanoTime() < deadline) {
diff --git a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
index 5cb0d7e..e632aaf 100644
--- a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
+++ b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
@@ -22,8 +22,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
-import android.net.SocketKeepalive.InvalidPacketException;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py
index 6b4c346..ca1847a 100755
--- a/tools/localedata/extract_icu_data.py
+++ b/tools/localedata/extract_icu_data.py
@@ -176,6 +176,9 @@
     dump_representative_locales(representative_locales)
     return likely_script_dict
 
+def escape_script_variable_name(script):
+    """Escape characters, e.g. '~', in a C++ variable name"""
+    return script.replace("~", "_")
 
 def read_parent_data(icu_data_dir):
     """Read locale parent data from ICU data files."""
@@ -221,7 +224,7 @@
     for script in sorted_scripts:
         parent_dict = script_organized_dict[script]
         print ('const std::unordered_map<uint32_t, uint32_t> %s_PARENTS({'
-            % script.upper())
+            % escape_script_variable_name(script.upper()))
         for locale in sorted(parent_dict.keys()):
             parent = parent_dict[locale]
             print '    {0x%08Xu, 0x%08Xu}, // %s -> %s' % (
@@ -239,7 +242,7 @@
     for script in sorted_scripts:
         print "    {{'%c', '%c', '%c', '%c'}, &%s_PARENTS}," % (
             script[0], script[1], script[2], script[3],
-            script.upper())
+            escape_script_variable_name(script.upper()))
     print '};'
 
 
diff --git a/wifi/Android.bp b/wifi/Android.bp
new file mode 100644
index 0000000..08115ec
--- /dev/null
+++ b/wifi/Android.bp
@@ -0,0 +1,87 @@
+// 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.
+
+
+filegroup {
+    name: "framework-wifi-updatable-sources",
+    srcs: [
+        "java/**/*.java",
+        "java/**/*.aidl",
+    ],
+    exclude_srcs: [
+        ":framework-wifi-non-updatable-sources"
+    ],
+    path: "java",
+}
+
+filegroup {
+    name: "framework-wifi-non-updatable-sources",
+    srcs: [
+        // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and
+        // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
+        // to a separate package.
+        "java/android/net/wifi/WifiNetworkScoreCache.java",
+        "java/android/net/wifi/WifiCondManager.java",
+        "java/android/net/wifi/wificond/*.java",
+        ":libwificond_ipc_aidl",
+    ],
+}
+
+filegroup {
+    name: "framework-wifi-annotations",
+    srcs: ["java/android/net/wifi/WifiAnnotations.java"],
+}
+
+java_library {
+    name: "framework-wifi",
+    sdk_version: "core_platform", // TODO(b/140299412) should be core_current
+    libs: [
+        "framework-minus-apex", // TODO(b/140299412) should be framework-system-stubs
+    ],
+    srcs: [
+        ":framework-wifi-updatable-sources",
+    ],
+    installable: true,
+    optimize: {
+        enabled: false
+    }
+}
+
+droidstubs {
+    name: "framework-wifi-stubs-srcs",
+    srcs: [
+        ":framework-annotations",
+        ":framework-wifi-updatable-sources",
+    ],
+    aidl: {
+        include_dirs: ["frameworks/base/core/java"],
+    },
+    defaults: [ "framework-module-stubs-defaults-systemapi" ],
+    sdk_version: "core_current",
+    libs: ["android_system_stubs_current"],
+}
+
+java_library {
+    name: "framework-wifi-stubs",
+    srcs: [":framework-wifi-stubs-srcs"],
+    aidl: {
+        export_include_dirs: [
+            "java",
+        ],
+    },
+    sdk_version: "core_current",
+    libs: ["android_system_stubs_current"],
+    installable: false,
+}
+
diff --git a/wifi/java/android/net/wifi/WifiAnnotations.java b/wifi/java/android/net/wifi/WifiAnnotations.java
new file mode 100644
index 0000000..4a7dee1
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiAnnotations.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.annotation.IntDef;
+import android.annotation.StringDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Wifi annotations meant to be statically linked into client modules, since they cannot be
+ * exposed as @SystemApi.
+ *
+ * e.g. {@link IntDef}, {@link StringDef}
+ *
+ * @hide
+ */
+public final class WifiAnnotations {
+    private WifiAnnotations() {}
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"SCAN_TYPE_"}, value = {
+            WifiScanner.SCAN_TYPE_LOW_LATENCY,
+            WifiScanner.SCAN_TYPE_LOW_POWER,
+            WifiScanner.SCAN_TYPE_HIGH_ACCURACY})
+    public @interface ScanType {}
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"WIFI_BAND_"}, value = {
+            WifiScanner.WIFI_BAND_UNSPECIFIED,
+            WifiScanner.WIFI_BAND_24_GHZ,
+            WifiScanner.WIFI_BAND_5_GHZ,
+            WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY,
+            WifiScanner.WIFI_BAND_6_GHZ})
+    public @interface WifiBandBasic {}
+}
diff --git a/wifi/java/android/net/wifi/WifiCondManager.java b/wifi/java/android/net/wifi/WifiCondManager.java
index 9ae7e3a..c05ba34 100644
--- a/wifi/java/android/net/wifi/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/WifiCondManager.java
@@ -725,7 +725,7 @@
     /**
      * Return scan type for the parcelable {@link SingleScanSettings}
      */
-    private static int getScanType(@WifiScanner.ScanType int scanType) {
+    private static int getScanType(@WifiAnnotations.ScanType int scanType) {
         switch (scanType) {
             case WifiScanner.SCAN_TYPE_LOW_LATENCY:
                 return IWifiScannerImpl.SCAN_TYPE_LOW_SPAN;
@@ -746,7 +746,7 @@
      * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
      * @return Returns true on success.
      */
-    public boolean scan(@NonNull String ifaceName, @WifiScanner.ScanType int scanType,
+    public boolean scan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
             Set<Integer> freqs, List<byte[]> hiddenNetworkSSIDs) {
         IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
         if (scannerImpl == null) {
@@ -868,7 +868,7 @@
      * @return frequencies vector of valid frequencies (MHz), or null for error.
      * @throws IllegalArgumentException if band is not recognized.
      */
-    public int [] getChannelsForBand(@WifiScanner.WifiBandBasic int band) {
+    public int [] getChannelsForBand(@WifiAnnotations.WifiBandBasic int band) {
         if (mWificond == null) {
             Log.e(TAG, "No valid wificond scanner interface handler");
             return null;
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 760497b..8fedda4 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -89,16 +89,6 @@
     /** 6 GHz band */
     public static final int WIFI_BAND_6_GHZ = 1 << WIFI_BAND_INDEX_6_GHZ;
 
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"WIFI_BAND_"}, value = {
-            WIFI_BAND_UNSPECIFIED,
-            WIFI_BAND_24_GHZ,
-            WIFI_BAND_5_GHZ,
-            WIFI_BAND_5_GHZ_DFS_ONLY,
-            WIFI_BAND_6_GHZ})
-    public @interface WifiBandBasic {}
-
     /**
      * Combination of bands
      * Note that those are only the common band combinations,
@@ -249,14 +239,6 @@
      */
     public static final int REPORT_EVENT_NO_BATCH = (1 << 2);
 
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"SCAN_TYPE_"}, value = {
-            SCAN_TYPE_LOW_LATENCY,
-            SCAN_TYPE_LOW_POWER,
-            SCAN_TYPE_HIGH_ACCURACY})
-    public @interface ScanType {}
-
     /**
      * Optimize the scan for lower latency.
      * @see ScanSettings#type
@@ -354,7 +336,7 @@
          * {@link #SCAN_TYPE_HIGH_ACCURACY}.
          * Default value: {@link #SCAN_TYPE_LOW_LATENCY}.
          */
-        @ScanType
+        @WifiAnnotations.ScanType
         @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
         public int type = SCAN_TYPE_LOW_LATENCY;
         /**
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
index 3453d6e..d2c385b4 100644
--- a/wifi/tests/Android.mk
+++ b/wifi/tests/Android.mk
@@ -59,6 +59,8 @@
 
 LOCAL_PACKAGE_NAME := FrameworksWifiApiTests
 LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_COMPATIBILITY_SUITE := device-tests
+LOCAL_COMPATIBILITY_SUITE := \
+    device-tests \
+    mts \
 
 include $(BUILD_PACKAGE)
diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml
index cae19e4..987fee7 100644
--- a/wifi/tests/AndroidTest.xml
+++ b/wifi/tests/AndroidTest.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Runs Frameworks Wifi API Tests.">
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="test-file-name" value="FrameworksWifiApiTests.apk" />
     </target_preparer>
 
diff --git a/wifi/tests/runtests.sh b/wifi/tests/runtests.sh
index 7a0dfb0..4024371 100755
--- a/wifi/tests/runtests.sh
+++ b/wifi/tests/runtests.sh
@@ -16,7 +16,6 @@
 
 set -x # print commands
 
-adb root
 adb wait-for-device
 
 TARGET_ARCH=$($ANDROID_BUILD_TOP/build/soong/soong_ui.bash --dumpvar-mode TARGET_ARCH)