Merge "Remove framework code that has moved to frameworks/libs/net"
diff --git a/Android.bp b/Android.bp
index 79a8a4b..84f3fad 100644
--- a/Android.bp
+++ b/Android.bp
@@ -368,6 +368,7 @@
         "devicepolicyprotosnano",
 
         "com.android.sysprop.apex",
+        "com.android.sysprop.init",
         "PlatformProperties",
     ],
     sdk_version: "core_platform",
@@ -396,7 +397,6 @@
         "ext",
         "unsupportedappusage",
         "updatable_media_stubs",
-        "framework-tethering",
     ],
 
     jarjar_rules: ":framework-jarjar-rules",
@@ -455,7 +455,9 @@
     name: "framework-minus-apex",
     defaults: ["framework-defaults"],
     srcs: [":framework-non-updatable-sources"],
-    libs: ["ike-stubs"],
+    libs: [
+        "framework-tethering-stubs",
+    ],
     installable: true,
     javac_shard_size: 150,
     required: [
@@ -471,6 +473,10 @@
     // For backwards compatibility.
     stem: "framework",
     apex_available: ["//apex_available:platform"],
+    visibility: [
+        // TODO(b/147128803) remove the below lines
+        "//frameworks/base/packages/Tethering/tests/unit",
+    ],
 }
 
 // This "framework" module is NOT installed to the device. It's
@@ -490,9 +496,7 @@
         "framework-minus-apex",
         "updatable_media_stubs",
         "framework-sdkextensions-stubs-systemapi",
-        // TODO(b/147200698): should be the stub of framework-tethering
-        "framework-tethering",
-        "ike-stubs",
+        "framework-tethering-stubs",
     ],
     sdk_version: "core_platform",
     apex_available: ["//apex_available:platform"],
@@ -608,7 +612,9 @@
         "core/java/android/annotation/Nullable.java",
         "core/java/android/annotation/IntDef.java",
         "core/java/android/annotation/IntRange.java",
+        "core/java/android/annotation/RequiresPermission.java",
         "core/java/android/annotation/SystemApi.java",
+        "core/java/android/annotation/TestApi.java",
         "core/java/com/android/internal/annotations/GuardedBy.java",
         "core/java/com/android/internal/annotations/VisibleForTesting.java",
     ],
@@ -671,17 +677,6 @@
     ],
 }
 
-filegroup {
-    name: "framework-tethering-annotations",
-    srcs: [
-        "core/java/android/annotation/NonNull.java",
-        "core/java/android/annotation/Nullable.java",
-        "core/java/android/annotation/RequiresPermission.java",
-        "core/java/android/annotation/SystemApi.java",
-        "core/java/android/annotation/TestApi.java",
-        "core/java/com/android/internal/annotations/GuardedBy.java",
-    ],
-}
 // Build ext.jar
 // ============================================================
 java_library {
@@ -915,22 +910,6 @@
     output_extension: "proto.h",
 }
 
-
-subdirs = [
-    "cmds/*",
-    "core/*",
-    "libs/*",
-    "media/*",
-    "proto",
-    "tools/*",
-    "native/android",
-    "native/graphics/jni",
-]
-
-optional_subdirs = [
-    "core/tests/utiltests/jni",
-]
-
 // TODO(b/77285514): remove this once the last few hidl interfaces have been
 // updated to use hwbinder.stubs.
 java_library {
@@ -990,13 +969,6 @@
 }
 
 filegroup {
-    name: "framework-annotation-nonnull-srcs",
-    srcs: [
-        "core/java/android/annotation/NonNull.java",
-    ],
-}
-
-filegroup {
     name: "framework-media-annotation-srcs",
     srcs: [
         "core/java/android/annotation/CallbackExecutor.java",
diff --git a/Android.mk b/Android.mk
index 815a169..09f2c40 100644
--- a/Android.mk
+++ b/Android.mk
@@ -75,12 +75,6 @@
 # Run this for checkbuild
 checkbuild: doc-comment-check-docs
 
-# ==== hiddenapi lists =======================================
-ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
-endif  # UNSAFE_DISABLE_HIDDENAPI_FLAGS
-
 # Include subdirectory makefiles
 # ============================================================
 
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 04ddc50..b7e3646 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -356,10 +356,10 @@
     dist: {
         targets: ["docs"],
     },
-    cmd: "unzip $(location :ds-docs-java{.docs.zip}) -d $(genDir) && " +
-         "unzip $(location :ds-docs-kt{.docs.zip}) -d $(genDir)/en/reference/kotlin && " +
+    cmd: "unzip -q $(location :ds-docs-java{.docs.zip}) -d $(genDir) && " +
+         "unzip -q $(location :ds-docs-kt{.docs.zip}) -d $(genDir)/en/reference/kotlin && " +
          "SWITCHER=$$(cd $$(dirname $(location switcher4)) && pwd)/$$(basename $(location switcher4)) && " +
-         "(cd $(genDir)/en/reference && $$SWITCHER --work platform) && " +
+         "(cd $(genDir)/en/reference && $$SWITCHER --work platform) > /dev/null && " +
          "$(location soong_zip) -o $(out) -C $(genDir) -D $(genDir)",
 }
 
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 8abe64c..84b3625 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -49,7 +49,6 @@
         ":opt-net-voip-srcs",
         ":core-current-stubs-source",
         ":core_public_api_files",
-        ":ike-api-srcs",
     ],
     libs: ["framework-internal-utils"],
     installable: false,
@@ -193,20 +192,17 @@
             api_file: "api/module-lib-current.txt",
             removed_api_file: "api/module-lib-removed.txt",
         },
-        // TODO(b/147559833) enable the compatibility check against the last release API
-        // and the API lint
-        //last_released: {
-        //    api_file: ":last-released-module-lib-api",
-        //    removed_api_file: "api/module-lib-removed.txt",
-        //    baseline_file: ":module-lib-api-incompatibilities-with-last-released"
-        //},
-        //api_lint: {
-        //    enabled: true,
-        //    new_since: ":last-released-module-lib-api",
-        //    baseline_file: "api/module-lib-lint-baseline.txt",
-        //},
+        last_released: {
+            api_file: ":last-released-module-lib-api",
+            removed_api_file: "api/module-lib-removed.txt",
+            baseline_file: ":module-lib-api-incompatibilities-with-last-released"
+        },
+        api_lint: {
+            enabled: true,
+            new_since: ":last-released-module-lib-api",
+            baseline_file: "api/module-lib-lint-baseline.txt",
+        },
     },
-    //jdiff_enabled: true,
 }
 
 
diff --git a/apex/Android.bp b/apex/Android.bp
index 362cf95..051986e 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -31,12 +31,12 @@
 
 priv_apps = " " +
     "--show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
     "\\) "
 
 module_libs = " " +
     " --show-annotation android.annotation.SystemApi\\(" +
-        "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
+        "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
     "\\) "
 
 stubs_defaults {
@@ -48,6 +48,7 @@
 stubs_defaults {
     name: "framework-module-stubs-defaults-systemapi",
     args: mainline_stubs_args + priv_apps,
+    srcs: [":framework-annotations"],
     installable: false,
 }
 
@@ -59,11 +60,13 @@
 stubs_defaults {
     name: "framework-module-api-defaults-module_libs_api",
     args: mainline_stubs_args + module_libs,
+    srcs: [":framework-annotations"],
     installable: false,
 }
 
 stubs_defaults {
     name: "framework-module-stubs-defaults-module_libs_api",
     args: mainline_stubs_args + module_libs + priv_apps,
+    srcs: [":framework-annotations"],
     installable: false,
 }
diff --git a/apex/sdkextensions/Android.bp b/apex/sdkextensions/Android.bp
index 4c5c2b2..a22a948 100644
--- a/apex/sdkextensions/Android.bp
+++ b/apex/sdkextensions/Android.bp
@@ -22,13 +22,14 @@
     binaries: [ "derive_sdk" ],
     prebuilts: [ "cur_sdkinfo" ],
     manifest: "manifest.json",
+    min_sdk_version: "current",
 }
 
 apex_defaults {
     name: "com.android.sdkext-defaults",
+    updatable: true,
     java_libs: [ "framework-sdkextensions" ],
     prebuilts: [
-        "com.android.sdkext.ldconfig",
         "derive_sdk.rc",
     ],
     key: "com.android.sdkext.key",
@@ -51,13 +52,6 @@
     certificate: "com.android.sdkext",
 }
 
-prebuilt_etc {
-    name: "com.android.sdkext.ldconfig",
-    src: "ld.config.txt",
-    filename: "ld.config.txt",
-    installable: false,
-}
-
 python_binary_host {
     name: "gen_sdkinfo",
     srcs: [
diff --git a/apex/sdkextensions/ld.config.txt b/apex/sdkextensions/ld.config.txt
deleted file mode 100644
index dcc69b8..0000000
--- a/apex/sdkextensions/ld.config.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Bionic loader config file for the sdkextensions apex.
-
-dir.sdkextensions = /apex/com.android.sdkext/bin/
-
-[sdkextensions]
-additional.namespaces = platform
-
-namespace.default.isolated = true
-namespace.default.links = platform
-namespace.default.link.platform.allow_all_shared_libs = true
-
-###############################################################################
-# "platform" namespace: used for NDK libraries
-###############################################################################
-namespace.platform.isolated = true
-namespace.platform.search.paths = /system/${LIB}
-namespace.platform.asan.search.paths = /data/asan/system/${LIB}
-
-# /system/lib/libc.so, etc are symlinks to /apex/com.android.lib/lib/bionic/libc.so, etc.
-# Add /apex/... path to the permitted paths because linker uses realpath(3)
-# to check the accessibility of the lib. We could add this to search.paths
-# instead but that makes the resolution of bionic libs be dependent on
-# the order of /system/lib and /apex/... in search.paths. If /apex/...
-# is after /system/lib, then /apex/... is never tried because libc.so
-# is always found in /system/lib but fails to pass the accessibility test
-# because of its realpath.  It's better to not depend on the ordering if
-# possible.
-namespace.platform.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
-namespace.platform.asan.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
diff --git a/api/current.txt b/api/current.txt
index 80a7bb4..e7877aa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11775,6 +11775,7 @@
     field public static final String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
     field @Deprecated public static final String FEATURE_VR_MODE = "android.software.vr.mode";
     field public static final String FEATURE_VR_MODE_HIGH_PERFORMANCE = "android.hardware.vr.high_performance";
+    field public static final String FEATURE_VULKAN_DEQP_LEVEL = "android.software.vulkan.deqp.level";
     field public static final String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
     field public static final String FEATURE_VULKAN_HARDWARE_LEVEL = "android.hardware.vulkan.level";
     field public static final String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version";
@@ -28356,6 +28357,7 @@
     method public int getAudioChannelCount();
     method public int getAudioSampleRate();
     method public CharSequence getDescription();
+    method @Nullable public String getEncoding();
     method public android.os.Bundle getExtra();
     method public String getId();
     method public String getLanguage();
@@ -28383,6 +28385,7 @@
     method @NonNull public android.media.tv.TvTrackInfo.Builder setAudioDescription(boolean);
     method public android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int);
     method public android.media.tv.TvTrackInfo.Builder setDescription(CharSequence);
+    method @NonNull public android.media.tv.TvTrackInfo.Builder setEncoding(@Nullable String);
     method @NonNull public android.media.tv.TvTrackInfo.Builder setEncrypted(boolean);
     method public android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
     method @NonNull public android.media.tv.TvTrackInfo.Builder setHardOfHearing(boolean);
@@ -29749,7 +29752,7 @@
 
   public class AudioGroup {
     ctor @Deprecated public AudioGroup();
-    ctor public AudioGroup(@Nullable android.content.Context);
+    ctor public AudioGroup(@NonNull android.content.Context);
     method public void clear();
     method public int getMode();
     method public android.net.rtp.AudioStream[] getStreams();
@@ -43833,7 +43836,7 @@
     method public final android.telecom.CallAudioState getCallAudioState();
     method public final String getCallerDisplayName();
     method public final int getCallerDisplayNamePresentation();
-    method public int getCallerNumberVerificationStatus();
+    method public final int getCallerNumberVerificationStatus();
     method public final android.telecom.Conference getConference();
     method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
     method public final int getConnectionCapabilities();
@@ -43886,7 +43889,7 @@
     method public final void setAudioModeIsVoip(boolean);
     method public final void setAudioRoute(int);
     method public final void setCallerDisplayName(String, int);
-    method public void setCallerNumberVerificationStatus(int);
+    method public final void setCallerNumberVerificationStatus(int);
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
     method public final void setConnectionCapabilities(int);
@@ -44701,8 +44704,8 @@
     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_HOLD_VIDEO_CALL_BOOL = "allow_hold_video_call_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";
     field public static final String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
@@ -45348,32 +45351,8 @@
 
   public final class PhoneCapability implements android.os.Parcelable {
     method public int describeContents();
-    method @NonNull public java.util.List<java.lang.Integer> getBands(int);
-    method @NonNull public java.util.List<java.util.List<java.lang.Long>> getConcurrentFeaturesSupport();
-    method @NonNull public java.util.List<java.lang.String> getLogicalModemUuids();
-    method public int getMaxActiveDedicatedBearers();
-    method public int getMaxActiveInternetData();
-    method public int getMaxActivePsVoice();
-    method public long getPsDataConnectionLingerTimeMillis();
-    method @NonNull public java.util.List<android.telephony.SimSlotCapability> getSimSlotCapabilities();
-    method public long getSupportedRats();
-    method public int getUeCategory(boolean, int);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneCapability> CREATOR;
-    field public static final long MODEM_FEATURE_3GPP2_REG = 1L; // 0x1L
-    field public static final long MODEM_FEATURE_3GPP_REG = 2L; // 0x2L
-    field public static final long MODEM_FEATURE_CDMA2000_EHRPD_REG = 4L; // 0x4L
-    field public static final long MODEM_FEATURE_CSIM = 8192L; // 0x2000L
-    field public static final long MODEM_FEATURE_CS_VOICE_SESSION = 512L; // 0x200L
-    field public static final long MODEM_FEATURE_DEDICATED_BEARER = 2048L; // 0x800L
-    field public static final long MODEM_FEATURE_EUTRAN_REG = 32L; // 0x20L
-    field public static final long MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG = 128L; // 0x80L
-    field public static final long MODEM_FEATURE_GERAN_REG = 8L; // 0x8L
-    field public static final long MODEM_FEATURE_INTERACTIVE_DATA_SESSION = 1024L; // 0x400L
-    field public static final long MODEM_FEATURE_NETWORK_SCAN = 4096L; // 0x1000L
-    field public static final long MODEM_FEATURE_NGRAN_REG = 64L; // 0x40L
-    field public static final long MODEM_FEATURE_PS_VOICE_REG = 256L; // 0x100L
-    field public static final long MODEM_FEATURE_UTRAN_REG = 16L; // 0x10L
   }
 
   public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher {
@@ -45558,18 +45537,6 @@
     field public static final int INVALID = 2147483647; // 0x7fffffff
   }
 
-  public final class SimSlotCapability implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getPhysicalSlotIndex();
-    method public int getSlotType();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SimSlotCapability> CREATOR;
-    field public static final int SLOT_TYPE_EUICC = 3; // 0x3
-    field public static final int SLOT_TYPE_IUICC = 2; // 0x2
-    field public static final int SLOT_TYPE_SOFT_SIM = 4; // 0x4
-    field public static final int SLOT_TYPE_UICC = 1; // 0x1
-  }
-
   public final class SmsManager {
     method public String createAppSpecificSmsToken(android.app.PendingIntent);
     method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent);
@@ -45916,7 +45883,6 @@
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode();
     method public String getNetworkSpecifier();
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType();
-    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.PhoneCapability getPhoneCapability();
     method @Deprecated public int getPhoneCount();
     method public int getPhoneType();
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
@@ -69783,6 +69749,7 @@
     method public static java.time.chrono.JapaneseEra[] values();
     field public static final java.time.chrono.JapaneseEra HEISEI;
     field public static final java.time.chrono.JapaneseEra MEIJI;
+    field public static final java.time.chrono.JapaneseEra REIWA;
     field public static final java.time.chrono.JapaneseEra SHOWA;
     field public static final java.time.chrono.JapaneseEra TAISHO;
   }
diff --git a/api/lint-baseline.txt b/api/lint-baseline.txt
index 5c31f41..b14a12f 100644
--- a/api/lint-baseline.txt
+++ b/api/lint-baseline.txt
@@ -537,6 +537,9 @@
     
 MissingNullability: android.icu.util.VersionInfo#UNICODE_13_0:
     Missing nullability on field `UNICODE_13_0` in class `class android.icu.util.VersionInfo`
+    
+MissingNullability: java.time.chrono.JapaneseEra#REIWA:
+    Missing nullability on field `REIWA` in class `class java.time.chrono.JapaneseEra`
 
 
 RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 2831924..1b72fc3 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -9,3 +9,123 @@
 
 }
 
+package android.net {
+
+  public final class TetheredClient implements android.os.Parcelable {
+    ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
+    method public int describeContents();
+    method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
+    method @NonNull public android.net.MacAddress getMacAddress();
+    method public int getTetheringType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
+  }
+
+  public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.net.LinkAddress getAddress();
+    method @Nullable public String getHostname();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
+  }
+
+  public class TetheringConstants {
+    field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+    field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+    field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+    field public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+    field public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+  }
+
+  public class TetheringManager {
+    ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier<android.os.IBinder>);
+    method public int getLastTetherError(@NonNull String);
+    method @NonNull public String[] getTetherableBluetoothRegexs();
+    method @NonNull public String[] getTetherableIfaces();
+    method @NonNull public String[] getTetherableUsbRegexs();
+    method @NonNull public String[] getTetherableWifiRegexs();
+    method @NonNull public String[] getTetheredIfaces();
+    method @NonNull public String[] getTetheringErroredIfaces();
+    method public boolean isTetheringSupported();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
+    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+    method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
+    method @Deprecated public int setUsbTethering(boolean);
+    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+    method @Deprecated public int tether(@NonNull String);
+    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+    method @Deprecated public int untether(@NonNull String);
+    field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
+    field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
+    field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+    field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+    field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+    field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+    field public static final int TETHERING_ETHERNET = 5; // 0x5
+    field public static final int TETHERING_INVALID = -1; // 0xffffffff
+    field public static final int TETHERING_NCM = 4; // 0x4
+    field public static final int TETHERING_USB = 1; // 0x1
+    field public static final int TETHERING_WIFI = 0; // 0x0
+    field public static final int TETHERING_WIFI_P2P = 3; // 0x3
+    field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
+    field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9
+    field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8
+    field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
+    field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
+    field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5
+    field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
+    field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
+    field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+    field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+    field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
+    field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
+    field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
+    field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
+    field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
+    field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
+  }
+
+  public static interface TetheringManager.OnTetheringEntitlementResultListener {
+    method public void onTetheringEntitlementResult(int);
+  }
+
+  public abstract static class TetheringManager.StartTetheringCallback {
+    ctor public TetheringManager.StartTetheringCallback();
+    method public void onTetheringFailed(int);
+    method public void onTetheringStarted();
+  }
+
+  public abstract static class TetheringManager.TetheringEventCallback {
+    ctor public TetheringManager.TetheringEventCallback();
+    method public void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+    method public void onError(@NonNull String, int);
+    method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+    method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public void onTetheringSupported(boolean);
+    method public void onUpstreamChanged(@Nullable android.net.Network);
+  }
+
+  @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
+    ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]);
+    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+  }
+
+  public static class TetheringManager.TetheringRequest {
+  }
+
+  public static class TetheringManager.TetheringRequest.Builder {
+    ctor public TetheringManager.TetheringRequest.Builder(int);
+    method @NonNull public android.net.TetheringManager.TetheringRequest build();
+    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean);
+    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress);
+  }
+
+}
+
diff --git a/api/module-lib-lint-baseline.txt b/api/module-lib-lint-baseline.txt
new file mode 100644
index 0000000..56f7a02
--- /dev/null
+++ b/api/module-lib-lint-baseline.txt
@@ -0,0 +1,29 @@
+// Baseline format: 1.0
+ActionValue: android.net.TetheringConstants#EXTRA_ADD_TETHER_TYPE:
+    Inconsistent extra value; expected `android.net.extra.ADD_TETHER_TYPE`, was `extraAddTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_PROVISION_CALLBACK:
+    Inconsistent extra value; expected `android.net.extra.PROVISION_CALLBACK`, was `extraProvisionCallback`
+ActionValue: android.net.TetheringConstants#EXTRA_REM_TETHER_TYPE:
+    Inconsistent extra value; expected `android.net.extra.REM_TETHER_TYPE`, was `extraRemTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_RUN_PROVISION:
+    Inconsistent extra value; expected `android.net.extra.RUN_PROVISION`, was `extraRunProvision`
+ActionValue: android.net.TetheringConstants#EXTRA_SET_ALARM:
+    Inconsistent extra value; expected `android.net.extra.SET_ALARM`, was `extraSetAlarm`
+ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
+    Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED`
+ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
+    Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray`
+ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
+    Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray`
+ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
+    Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray`
+
+
+ManagerConstructor: android.net.TetheringManager#TetheringManager(android.content.Context, java.util.function.Supplier<android.os.IBinder>):
+    Managers must always be obtained from Context; no direct constructors
+
+
+PrivateSuperclass: android.location.GnssAntennaInfo.PhaseCenterVariationCorrections:
+    Public class android.location.GnssAntennaInfo.PhaseCenterVariationCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
+PrivateSuperclass: android.location.GnssAntennaInfo.SignalGainCorrections:
+    Public class android.location.GnssAntennaInfo.SignalGainCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
diff --git a/api/system-current.txt b/api/system-current.txt
index 202d51f..764a386 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -130,6 +130,7 @@
     field public static final String NETWORK_FACTORY = "android.permission.NETWORK_FACTORY";
     field public static final String NETWORK_MANAGED_PROVISIONING = "android.permission.NETWORK_MANAGED_PROVISIONING";
     field public static final String NETWORK_SCAN = "android.permission.NETWORK_SCAN";
+    field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
     field public static final String NETWORK_SETUP_WIZARD = "android.permission.NETWORK_SETUP_WIZARD";
     field public static final String NETWORK_SIGNAL_STRENGTH_WAKEUP = "android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP";
     field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
@@ -1448,15 +1449,17 @@
   }
 
   public final class BluetoothHidHost implements android.bluetooth.BluetoothProfile {
-    method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
-    method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
   }
 
-  public final class BluetoothMap implements android.bluetooth.BluetoothProfile {
-    method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+  public final class BluetoothMap implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void close();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) protected void finalize();
+    method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
     field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
@@ -3349,8 +3352,14 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
     field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
     field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
+    field public static final long FUNCTION_ACCESSORY = 2L; // 0x2L
+    field public static final long FUNCTION_ADB = 1L; // 0x1L
+    field public static final long FUNCTION_AUDIO_SOURCE = 64L; // 0x40L
+    field public static final long FUNCTION_MIDI = 8L; // 0x8L
+    field public static final long FUNCTION_MTP = 4L; // 0x4L
     field public static final long FUNCTION_NCM = 1024L; // 0x400L
     field public static final long FUNCTION_NONE = 0L; // 0x0L
+    field public static final long FUNCTION_PTP = 16L; // 0x10L
     field public static final long FUNCTION_RNDIS = 32L; // 0x20L
     field public static final String USB_CONFIGURED = "configured";
     field public static final String USB_CONNECTED = "connected";
@@ -4405,13 +4414,13 @@
   public class ConnectivityManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
     method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
-    method @Deprecated @RequiresPermission("android.permission.NETWORK_SETTINGS") public String getCaptivePortalServerUrl();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
     method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
     method @Deprecated public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int, int, @NonNull android.os.Handler);
-    method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
     method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
@@ -4447,7 +4456,7 @@
   }
 
   public class EthernetManager {
-    method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull android.net.EthernetManager.TetheredInterfaceCallback);
+    method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
   }
 
   public static interface EthernetManager.TetheredInterfaceCallback {
@@ -4616,6 +4625,10 @@
     method public void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
     method public void sendNetworkScore(int);
     method public void sendSocketKeepaliveEvent(int, int);
+    method public void setConnected();
+    method @Deprecated public void setLegacyExtraInfo(@Nullable String);
+    method @Deprecated public void setLegacySubtype(int, @NonNull String);
+    method public void unregister();
     field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
     field public static final int VALIDATION_STATUS_VALID = 1; // 0x1
     field @NonNull public final android.net.Network network;
@@ -4994,337 +5007,6 @@
 
 }
 
-package android.net.eap {
-
-  public final class EapSessionConfig {
-  }
-
-  public static final class EapSessionConfig.Builder {
-    ctor public EapSessionConfig.Builder();
-    method @NonNull public android.net.eap.EapSessionConfig build();
-    method @NonNull public android.net.eap.EapSessionConfig.Builder setEapAkaConfig(int, int);
-    method @NonNull public android.net.eap.EapSessionConfig.Builder setEapAkaPrimeConfig(int, int, @NonNull String, boolean);
-    method @NonNull public android.net.eap.EapSessionConfig.Builder setEapIdentity(@NonNull byte[]);
-    method @NonNull public android.net.eap.EapSessionConfig.Builder setEapMsChapV2Config(@NonNull String, @NonNull String);
-    method @NonNull public android.net.eap.EapSessionConfig.Builder setEapSimConfig(int, int);
-  }
-
-  public static class EapSessionConfig.EapAkaConfig extends android.net.eap.EapSessionConfig.EapUiccConfig {
-  }
-
-  public static class EapSessionConfig.EapAkaPrimeConfig extends android.net.eap.EapSessionConfig.EapAkaConfig {
-    method public boolean allowsMismatchedNetworkNames();
-    method @NonNull public String getNetworkName();
-  }
-
-  public abstract static class EapSessionConfig.EapMethodConfig {
-    method public int getMethodType();
-  }
-
-  public static class EapSessionConfig.EapMsChapV2Config extends android.net.eap.EapSessionConfig.EapMethodConfig {
-    method @NonNull public String getPassword();
-    method @NonNull public String getUsername();
-  }
-
-  public static class EapSessionConfig.EapSimConfig extends android.net.eap.EapSessionConfig.EapUiccConfig {
-  }
-
-  public abstract static class EapSessionConfig.EapUiccConfig extends android.net.eap.EapSessionConfig.EapMethodConfig {
-    method public int getAppType();
-    method public int getSubId();
-  }
-
-}
-
-package android.net.ipsec.ike {
-
-  public final class ChildSaProposal extends android.net.ipsec.ike.SaProposal {
-  }
-
-  public static final class ChildSaProposal.Builder {
-    ctor public ChildSaProposal.Builder();
-    method @NonNull public android.net.ipsec.ike.ChildSaProposal.Builder addDhGroup(int);
-    method @NonNull public android.net.ipsec.ike.ChildSaProposal.Builder addEncryptionAlgorithm(int, int);
-    method @NonNull public android.net.ipsec.ike.ChildSaProposal.Builder addIntegrityAlgorithm(int);
-    method @NonNull public android.net.ipsec.ike.ChildSaProposal build();
-  }
-
-  public interface ChildSessionCallback {
-    method public void onClosed();
-    method public void onClosedExceptionally(@NonNull android.net.ipsec.ike.exceptions.IkeException);
-    method public void onIpSecTransformCreated(@NonNull android.net.IpSecTransform, int);
-    method public void onIpSecTransformDeleted(@NonNull android.net.IpSecTransform, int);
-    method public void onOpened(@NonNull android.net.ipsec.ike.ChildSessionConfiguration);
-  }
-
-  public final class ChildSessionConfiguration {
-    method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getInboundTrafficSelectors();
-    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.IpPrefix> getInternalSubnets();
-    method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getOutboundTrafficSelectors();
-  }
-
-  public abstract class ChildSessionParams {
-    method public long getHardLifetime();
-    method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getLocalTrafficSelectors();
-    method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getRemoteTrafficSelectors();
-    method @NonNull public java.util.List<android.net.ipsec.ike.ChildSaProposal> getSaProposals();
-    method public long getSoftLifetime();
-  }
-
-  public class IkeFqdnIdentification extends android.net.ipsec.ike.IkeIdentification {
-    ctor public IkeFqdnIdentification(@NonNull String);
-    field @NonNull public final String fqdn;
-  }
-
-  public abstract class IkeIdentification {
-  }
-
-  public final class IkeIpv4AddrIdentification extends android.net.ipsec.ike.IkeIdentification {
-    ctor public IkeIpv4AddrIdentification(@NonNull java.net.Inet4Address);
-    field @NonNull public final java.net.Inet4Address ipv4Address;
-  }
-
-  public class IkeIpv6AddrIdentification extends android.net.ipsec.ike.IkeIdentification {
-    ctor public IkeIpv6AddrIdentification(@NonNull java.net.Inet6Address);
-    field @NonNull public final java.net.Inet6Address ipv6Address;
-  }
-
-  public final class IkeKeyIdIdentification extends android.net.ipsec.ike.IkeIdentification {
-    ctor public IkeKeyIdIdentification(@NonNull byte[]);
-    field @NonNull public final byte[] keyId;
-  }
-
-  public final class IkeRfc822AddrIdentification extends android.net.ipsec.ike.IkeIdentification {
-    ctor public IkeRfc822AddrIdentification(@NonNull String);
-    field @NonNull public final String rfc822Name;
-  }
-
-  public final class IkeSaProposal extends android.net.ipsec.ike.SaProposal {
-    method @NonNull public java.util.List<java.lang.Integer> getPseudorandomFunctions();
-  }
-
-  public static final class IkeSaProposal.Builder {
-    ctor public IkeSaProposal.Builder();
-    method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addDhGroup(int);
-    method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addEncryptionAlgorithm(int, int);
-    method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addIntegrityAlgorithm(int);
-    method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addPseudorandomFunction(int);
-    method @NonNull public android.net.ipsec.ike.IkeSaProposal build();
-  }
-
-  public final class IkeSession implements java.lang.AutoCloseable {
-    ctor public IkeSession(@NonNull android.content.Context, @NonNull android.net.ipsec.ike.IkeSessionParams, @NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull java.util.concurrent.Executor, @NonNull android.net.ipsec.ike.IkeSessionCallback, @NonNull android.net.ipsec.ike.ChildSessionCallback);
-    method public void close();
-    method public void closeChildSession(@NonNull android.net.ipsec.ike.ChildSessionCallback);
-    method public void kill();
-    method public void openChildSession(@NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull android.net.ipsec.ike.ChildSessionCallback);
-  }
-
-  public interface IkeSessionCallback {
-    method public void onClosed();
-    method public void onClosedExceptionally(@NonNull android.net.ipsec.ike.exceptions.IkeException);
-    method public void onError(@NonNull android.net.ipsec.ike.exceptions.IkeProtocolException);
-    method public void onOpened(@NonNull android.net.ipsec.ike.IkeSessionConfiguration);
-  }
-
-  public final class IkeSessionConfiguration {
-    method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
-    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
-  }
-
-  public final class IkeSessionParams {
-    method @NonNull public java.util.List<android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest> getConfigurationRequests();
-    method public long getHardLifetime();
-    method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getLocalAuthConfig();
-    method @NonNull public android.net.ipsec.ike.IkeIdentification getLocalIdentification();
-    method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getRemoteAuthConfig();
-    method @NonNull public android.net.ipsec.ike.IkeIdentification getRemoteIdentification();
-    method @NonNull public java.util.List<android.net.ipsec.ike.IkeSaProposal> getSaProposals();
-    method @NonNull public java.net.InetAddress getServerAddress();
-    method public long getSoftLifetime();
-    method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket getUdpEncapsulationSocket();
-  }
-
-  public static final class IkeSessionParams.Builder {
-    ctor public IkeSessionParams.Builder();
-    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(@NonNull java.net.InetAddress);
-    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(int);
-    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(@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 setLifetime(long, long);
-    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);
-    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setServerAddress(@NonNull java.net.InetAddress);
-    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket);
-  }
-
-  public static interface IkeSessionParams.ConfigRequestIpv4PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest {
-    method @Nullable public java.net.Inet4Address getAddress();
-  }
-
-  public static interface IkeSessionParams.ConfigRequestIpv6PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest {
-    method @Nullable public java.net.Inet6Address getAddress();
-  }
-
-  public abstract static class IkeSessionParams.IkeAuthConfig {
-  }
-
-  public static class IkeSessionParams.IkeAuthDigitalSignLocalConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
-    method @NonNull public java.security.cert.X509Certificate getClientEndCertificate();
-    method @NonNull public java.util.List<java.security.cert.X509Certificate> getIntermediateCertificates();
-    method @NonNull public java.security.PrivateKey getPrivateKey();
-  }
-
-  public static class IkeSessionParams.IkeAuthDigitalSignRemoteConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
-    method @Nullable public java.security.cert.X509Certificate getRemoteCaCert();
-  }
-
-  public static class IkeSessionParams.IkeAuthEapConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
-    method @NonNull public android.net.eap.EapSessionConfig getEapConfig();
-  }
-
-  public static class IkeSessionParams.IkeAuthPskConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
-    method @NonNull public byte[] getPsk();
-  }
-
-  public static interface IkeSessionParams.IkeConfigRequest {
-  }
-
-  public final class IkeTrafficSelector {
-    ctor public IkeTrafficSelector(int, int, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress);
-    field public final int endPort;
-    field @NonNull public final java.net.InetAddress endingAddress;
-    field public final int startPort;
-    field @NonNull public final java.net.InetAddress startingAddress;
-  }
-
-  public abstract class SaProposal {
-    method @NonNull public java.util.List<java.lang.Integer> getDhGroups();
-    method @NonNull public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getEncryptionAlgorithms();
-    method @NonNull public java.util.List<java.lang.Integer> getIntegrityAlgorithms();
-    field public static final int DH_GROUP_1024_BIT_MODP = 2; // 0x2
-    field public static final int DH_GROUP_2048_BIT_MODP = 14; // 0xe
-    field public static final int DH_GROUP_NONE = 0; // 0x0
-    field public static final int ENCRYPTION_ALGORITHM_3DES = 3; // 0x3
-    field public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; // 0xc
-    field public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = 19; // 0x13
-    field public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = 20; // 0x14
-    field public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = 18; // 0x12
-    field public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; // 0x5
-    field public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; // 0x2
-    field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; // 0xc
-    field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; // 0xd
-    field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; // 0xe
-    field public static final int INTEGRITY_ALGORITHM_NONE = 0; // 0x0
-    field public static final int KEY_LEN_AES_128 = 128; // 0x80
-    field public static final int KEY_LEN_AES_192 = 192; // 0xc0
-    field public static final int KEY_LEN_AES_256 = 256; // 0x100
-    field public static final int KEY_LEN_UNUSED = 0; // 0x0
-    field public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; // 0x4
-    field public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; // 0x2
-  }
-
-  public final class TransportModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
-  }
-
-  public static final class TransportModeChildSessionParams.Builder {
-    ctor public TransportModeChildSessionParams.Builder();
-    method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
-    method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
-    method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
-    method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams build();
-    method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder setLifetime(long, long);
-  }
-
-  public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
-    method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest> getConfigurationRequests();
-  }
-
-  public static final class TunnelModeChildSessionParams.Builder {
-    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.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 addInternalDnsServerRequest(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();
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder setLifetime(long, long);
-  }
-
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
-    method @Nullable public java.net.Inet4Address getAddress();
-  }
-
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
-    method @Nullable public java.net.Inet4Address getAddress();
-  }
-
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
-    method @Nullable public java.net.Inet4Address getAddress();
-  }
-
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
-  }
-
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
-    method @Nullable public java.net.Inet6Address getAddress();
-    method public int getPrefixLength();
-  }
-
-  public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
-    method @Nullable public java.net.Inet6Address getAddress();
-  }
-
-  public static interface TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
-  }
-
-}
-
-package android.net.ipsec.ike.exceptions {
-
-  public abstract class IkeException extends java.lang.Exception {
-  }
-
-  public final class IkeInternalException extends android.net.ipsec.ike.exceptions.IkeException {
-  }
-
-  public abstract class IkeProtocolException extends android.net.ipsec.ike.exceptions.IkeException {
-    method @Nullable public byte[] getErrorData();
-    method public int getErrorType();
-    field public static final int ERROR_TYPE_AUTHENTICATION_FAILED = 24; // 0x18
-    field public static final int ERROR_TYPE_CHILD_SA_NOT_FOUND = 44; // 0x2c
-    field public static final int ERROR_TYPE_FAILED_CP_REQUIRED = 37; // 0x25
-    field public static final int ERROR_TYPE_INTERNAL_ADDRESS_FAILURE = 36; // 0x24
-    field public static final int ERROR_TYPE_INVALID_IKE_SPI = 4; // 0x4
-    field public static final int ERROR_TYPE_INVALID_KE_PAYLOAD = 17; // 0x11
-    field public static final int ERROR_TYPE_INVALID_MAJOR_VERSION = 5; // 0x5
-    field public static final int ERROR_TYPE_INVALID_MESSAGE_ID = 9; // 0x9
-    field public static final int ERROR_TYPE_INVALID_SELECTORS = 39; // 0x27
-    field public static final int ERROR_TYPE_INVALID_SYNTAX = 7; // 0x7
-    field public static final int ERROR_TYPE_NO_ADDITIONAL_SAS = 35; // 0x23
-    field public static final int ERROR_TYPE_NO_PROPOSAL_CHOSEN = 14; // 0xe
-    field public static final int ERROR_TYPE_SINGLE_PAIR_REQUIRED = 34; // 0x22
-    field public static final int ERROR_TYPE_TEMPORARY_FAILURE = 43; // 0x2b
-    field public static final int ERROR_TYPE_TS_UNACCEPTABLE = 38; // 0x26
-    field public static final int ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD = 1; // 0x1
-  }
-
-}
-
 package android.net.metrics {
 
   public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
@@ -5784,13 +5466,13 @@
 
   public class WifiManager {
     method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
-    method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
-    method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
-    method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
-    method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener);
-    method @NonNull @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
-    method @NonNull @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
-    method @NonNull @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
@@ -5798,17 +5480,17 @@
     method public boolean isPortableHotspotSupported();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
     method public boolean isWifiScannerSupported();
-    method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
     method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
-    method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
     method @RequiresPermission("android.permission.WIFI_SET_DEVICE_MOBILITY_STATE") public void setDeviceMobilityState(int);
     method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
-    method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
-    method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startSoftAp(@Nullable android.net.wifi.WifiConfiguration);
-    method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback);
-    method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession();
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateInterfaceIpState(@Nullable String, int);
     method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void updateWifiUsabilityScore(int, int, int);
@@ -7692,28 +7374,6 @@
     method @NonNull @WorkerThread public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
   }
 
-  public abstract class CarrierMessagingServiceWrapper {
-    ctor public CarrierMessagingServiceWrapper();
-    method public boolean bindToCarrierMessagingService(@NonNull android.content.Context, @NonNull String);
-    method public void disposeConnection(@NonNull android.content.Context);
-    method public void downloadMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
-    method public void filterSms(@NonNull android.service.carrier.MessagePdu, @NonNull String, int, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
-    method public abstract void onServiceReady();
-    method public void sendDataSms(@NonNull byte[], int, @NonNull String, int, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
-    method public void sendMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
-    method public void sendMultipartTextSms(@NonNull java.util.List<java.lang.String>, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
-    method public void sendTextSms(@NonNull String, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
-  }
-
-  public abstract static class CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper {
-    ctor public CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper();
-    method public void onDownloadMmsComplete(int);
-    method public void onFilterComplete(int);
-    method public void onSendMmsComplete(int, @Nullable byte[]);
-    method public void onSendMultipartSmsComplete(int, @Nullable int[]);
-    method public void onSendSmsComplete(int, int);
-  }
-
 }
 
 package android.service.contentcapture {
@@ -9416,6 +9076,7 @@
     method public void fillInNotifierBundle(@NonNull android.os.Bundle);
     method public int getDataNetworkType();
     method public int getDataRegistrationState();
+    method public boolean getDataRoamingFromRegistration();
     method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int);
     method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int);
     method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(int);
@@ -9583,6 +9244,8 @@
   public class SubscriptionManager {
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean canDisablePhysicalSubscription();
     method public boolean canManageSubscription(@Nullable android.telephony.SubscriptionInfo, @Nullable String);
+    method @NonNull public int[] getActiveAndHiddenSubscriptionIdList();
+    method @NonNull public int[] getActiveSubscriptionIdList();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
     method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
@@ -9707,6 +9370,7 @@
     method public boolean isDataConnectivityPossible();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isGlobalModeEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
@@ -9868,16 +9532,6 @@
     field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0
   }
 
-  public class TelephonyRegistryManager {
-    method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
-    method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String);
-    method public void notifyCarrierNetworkChange(boolean);
-    method public void notifyRegistrationFailed(int, int, @NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
-    method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
-    method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
-  }
-
   public final class UiccAccessRule implements android.os.Parcelable {
     ctor public UiccAccessRule(byte[], @Nullable String, long);
     method public int describeContents();
diff --git a/api/test-current.txt b/api/test-current.txt
index b3e80a9..c2553ed 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -14,6 +14,7 @@
     field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
     field public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
+    field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
     field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
     field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
@@ -1439,7 +1440,7 @@
   }
 
   public class EthernetManager {
-    method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull android.net.EthernetManager.TetheredInterfaceCallback);
+    method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
   }
 
   public static interface EthernetManager.TetheredInterfaceCallback {
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 8ee79f6..d723776 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -113,10 +113,10 @@
 Status Idmap2Service::createIdmap(const std::string& target_apk_path,
                                   const std::string& overlay_apk_path, int32_t fulfilled_policies,
                                   bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED,
-                                  std::unique_ptr<std::string>* _aidl_return) {
+                                  std::optional<std::string>* _aidl_return) {
   assert(_aidl_return);
   SYSTRACE << "Idmap2Service::createIdmap " << target_apk_path << " " << overlay_apk_path;
-  _aidl_return->reset(nullptr);
+  _aidl_return->reset();
 
   const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies);
 
@@ -155,7 +155,7 @@
     return error("failed to write to idmap path " + idmap_path);
   }
 
-  *_aidl_return = std::make_unique<std::string>(idmap_path);
+  *_aidl_return = idmap_path;
   return ok();
 }
 
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 1aab059..73a236a 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -20,7 +20,7 @@
 #include <android-base/unique_fd.h>
 #include <binder/BinderService.h>
 
-#include <memory>
+#include <optional>
 #include <string>
 
 #include "android/os/BnIdmap2.h"
@@ -45,7 +45,7 @@
   binder::Status createIdmap(const std::string& target_apk_path,
                              const std::string& overlay_apk_path, int32_t fulfilled_policies,
                              bool enforce_overlayable, int32_t user_id,
-                             std::unique_ptr<std::string>* _aidl_return);
+                             std::optional<std::string>* _aidl_return);
 };
 
 }  // namespace android::os
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index c9e026b..af4f675 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -320,11 +320,6 @@
                                mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
     if (newMetricsManager->isConfigValid()) {
         mUidMap->OnConfigUpdated(key);
-        if (newMetricsManager->shouldAddUidMapListener()) {
-            // We have to add listener after the MetricsManager is constructed because it's
-            // not safe to create wp or sp from this pointer inside its constructor.
-            mUidMap->addListener(newMetricsManager.get());
-        }
         newMetricsManager->refreshTtl(timestampNs);
         mMetricsManagers[key] = newMetricsManager;
         VLOG("StatsdConfig valid");
@@ -743,6 +738,32 @@
     }
 }
 
+void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk,
+                                         const int uid, const int64_t version) {
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
+    ALOGW("Received app upgrade");
+    for (auto it : mMetricsManagers) {
+        it.second->notifyAppUpgrade(eventTimeNs, apk, uid, version);
+    }
+}
+
+void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
+                                         const int uid) {
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
+    ALOGW("Received app removed");
+    for (auto it : mMetricsManagers) {
+        it.second->notifyAppRemoved(eventTimeNs, apk, uid);
+    }
+}
+
+void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) {
+    std::lock_guard<std::mutex> lock(mMetricsMutex);
+    ALOGW("Received uid map");
+    for (auto it : mMetricsManagers) {
+        it.second->onUidMapReceived(eventTimeNs);
+    }
+}
+
 void StatsLogProcessor::noteOnDiskData(const ConfigKey& key) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     mOnDiskDataConfigs.insert(key);
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 313e16d..0d2b33e 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -32,7 +32,7 @@
 namespace statsd {
 
 
-class StatsLogProcessor : public ConfigListener {
+class StatsLogProcessor : public ConfigListener, public virtual PackageInfoListener {
 public:
     StatsLogProcessor(const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerManager,
                       const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -91,6 +91,16 @@
     /* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */
     void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs);
 
+    /* Notify all MetricsManagers of app upgrades */
+    void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
+                          const int64_t version) override;
+
+    /* Notify all MetricsManagers of app removals */
+    void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override;
+
+    /* Notify all MetricsManagers of uid map snapshots received */
+    void onUidMapReceived(const int64_t& eventTimeNs) override;
+
     // Reset all configs.
     void resetConfigs();
 
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index d6c1380..dd65155 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -200,6 +200,7 @@
                 }
             });
 
+    mUidMap->setListener(mProcessor);
     mConfigManager->AddListener(mProcessor);
 
     init_system_properties();
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 6cd34ae..7a63493 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -355,7 +355,7 @@
         BootTimeEventElapsedTime boot_time_event_elapsed_time_reported = 240;
         BootTimeEventUtcTime boot_time_event_utc_time_reported = 241;
         BootTimeEventErrorCode boot_time_event_error_code_reported = 242;
-        UserspaceRebootReported userspace_reboot_reported = 243;
+        UserspaceRebootReported userspace_reboot_reported = 243 [(log_from_module) = "framework"];
     }
 
     // Pulled events will start at field 10000.
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 09ad290..a0c8224 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -73,7 +73,7 @@
 // writing the report to dropbox. MetricProducers should respond to package changes as required in
 // PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
 // be a no-op.
-class MetricProducer : public virtual PackageInfoListener {
+class MetricProducer : public virtual android::RefBase {
 public:
     MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
                    const int conditionIndex, const sp<ConditionWizard>& wizard)
@@ -105,8 +105,8 @@
      * the flush again when the end timestamp is forced to be now, and then after flushing, update
      * the start timestamp to be now.
      */
-    void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
-                          const int64_t version) override {
+    virtual void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
+                          const int64_t version) {
         std::lock_guard<std::mutex> lock(mMutex);
 
         if (eventTimeNs > getCurrentBucketEndTimeNs()) {
@@ -119,16 +119,11 @@
         // is a partial bucket and can merge it with the previous bucket.
     };
 
-    void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override{
+    void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) {
         // Force buckets to split on removal also.
         notifyAppUpgrade(eventTimeNs, apk, uid, 0);
     };
 
-    void onUidMapReceived(const int64_t& eventTimeNs) override{
-            // Purposefully don't flush partial buckets on a new snapshot.
-            // This occurs if a new user is added/removed or statsd crashes.
-    };
-
     // Consume the parsed stats log entry that already matched the "what" of the metric.
     void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
         std::lock_guard<std::mutex> lock(mMutex);
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 7b7d0ca..760e800 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -175,6 +175,10 @@
 
 void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
                                       const int64_t version) {
+    // Inform all metric producers.
+    for (auto it : mAllMetricProducers) {
+        it->notifyAppUpgrade(eventTimeNs, apk, uid, version);
+    }
     // check if we care this package
     if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
         return;
@@ -186,6 +190,10 @@
 
 void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
                                       const int uid) {
+    // Inform all metric producers.
+    for (auto it : mAllMetricProducers) {
+        it->notifyAppRemoved(eventTimeNs, apk, uid);
+    }
     // check if we care this package
     if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
         return;
@@ -196,6 +204,9 @@
 }
 
 void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) {
+    // Purposefully don't inform metric producers on a new snapshot
+    // because we don't need to flush partial buckets.
+    // This occurs if a new user is added/removed or statsd crashes.
     if (mAllowedPkg.size() == 0) {
         return;
     }
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 8efca1e..34d47d4 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -35,7 +35,7 @@
 namespace statsd {
 
 // A MetricsManager is responsible for managing metrics from one single config source.
-class MetricsManager : public PackageInfoListener {
+class MetricsManager : public virtual android::RefBase {
 public:
     MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const int64_t timeBaseNs,
                    const int64_t currentTimeNs, const sp<UidMap>& uidMap,
@@ -63,15 +63,11 @@
         unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
 
     void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
-                          const int64_t version) override;
+                          const int64_t version);
 
-    void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override;
+    void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid);
 
-    void onUidMapReceived(const int64_t& eventTimeNs) override;
-
-    bool shouldAddUidMapListener() const {
-        return !mAllowedPkg.empty();
-    }
+    void onUidMapReceived(const int64_t& eventTimeNs);
 
     bool shouldWriteToDisk() const {
         return mNoReportMetricIds.size() != mAllMetricProducers.size();
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 46442b5..f5f2479 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -286,7 +286,7 @@
 }
 
 bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
-                 const int64_t currentTimeNs, UidMap& uidMap,
+                 const int64_t currentTimeNs,
                  const sp<StatsPullerManager>& pullerManager,
                  const unordered_map<int64_t, int>& logTrackerMap,
                  const unordered_map<int64_t, int>& conditionTrackerMap,
@@ -600,9 +600,6 @@
         }
         noReportMetricIds.insert(no_report_metric);
     }
-    for (const auto& it : allMetricProducers) {
-        uidMap.addListener(it);
-    }
     return true;
 }
 
@@ -807,7 +804,7 @@
         return false;
     }
 
-    if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap, pullerManager, logTrackerMap,
+    if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, logTrackerMap,
                      conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers,
                      conditionToMetricMap, trackerToMetricMap, metricProducerMap,
                      noReportMetricIds)) {
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index d4b57dd..7e63bbf 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -119,7 +119,7 @@
 void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
                        const vector<int64_t>& versionCode, const vector<String16>& versionString,
                        const vector<String16>& packageName, const vector<String16>& installer) {
-    vector<wp<PackageInfoListener>> broadcastList;
+    wp<PackageInfoListener> broadcast = NULL;
     {
         lock_guard<mutex> lock(mMutex);  // Exclusively lock for updates.
 
@@ -150,25 +150,22 @@
 
         ensureBytesUsedBelowLimit();
         StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
-        getListenerListCopyLocked(&broadcastList);
+        broadcast = mSubscriber;
     }
     // To avoid invoking callback while holding the internal lock. we get a copy of the listener
-    // list and invoke the callback. It's still possible that after we copy the list, a
-    // listener removes itself before we call it. It's then the listener's job to handle it (expect
-    // the callback to be called after listener is removed, and the listener should properly
-    // ignore it).
-    for (const auto& weakPtr : broadcastList) {
-        auto strongPtr = weakPtr.promote();
-        if (strongPtr != NULL) {
-            strongPtr->onUidMapReceived(timestamp);
-        }
+    // and invoke the callback. It's still possible that after we copy the listener, it removes
+    // itself before we call it. It's then the listener's job to handle it (expect the callback to
+    // be called after listener is removed, and the listener should properly ignore it).
+    auto strongPtr = broadcast.promote();
+    if (strongPtr != NULL) {
+        strongPtr->onUidMapReceived(timestamp);
     }
 }
 
 void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid,
                        const int64_t& versionCode, const String16& versionString,
                        const String16& installer) {
-    vector<wp<PackageInfoListener>> broadcastList;
+    wp<PackageInfoListener> broadcast = NULL;
     string appName = string(String8(app_16).string());
     {
         lock_guard<mutex> lock(mMutex);
@@ -195,7 +192,7 @@
             // for the first time, then we don't notify the listeners.
             // It's also OK to split again if we're forming a partial bucket after re-installing an
             // app after deletion.
-            getListenerListCopyLocked(&broadcastList);
+            broadcast = mSubscriber;
         }
         mChanges.emplace_back(false, timestamp, appName, uid, versionCode, newVersionString,
                               prevVersion, prevVersionString);
@@ -205,11 +202,9 @@
         StatsdStats::getInstance().setUidMapChanges(mChanges.size());
     }
 
-    for (const auto& weakPtr : broadcastList) {
-        auto strongPtr = weakPtr.promote();
-        if (strongPtr != NULL) {
-            strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode);
-        }
+    auto strongPtr = broadcast.promote();
+    if (strongPtr != NULL) {
+        strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode);
     }
 }
 
@@ -230,21 +225,8 @@
     }
 }
 
-void UidMap::getListenerListCopyLocked(vector<wp<PackageInfoListener>>* output) {
-    for (auto weakIt = mSubscribers.begin(); weakIt != mSubscribers.end();) {
-        auto strongPtr = weakIt->promote();
-        if (strongPtr != NULL) {
-            output->push_back(*weakIt);
-            weakIt++;
-        } else {
-            weakIt = mSubscribers.erase(weakIt);
-            VLOG("The UidMap listener is gone, remove it now");
-        }
-    }
-}
-
 void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid) {
-    vector<wp<PackageInfoListener>> broadcastList;
+    wp<PackageInfoListener> broadcast = NULL;
     string app = string(String8(app_16).string());
     {
         lock_guard<mutex> lock(mMutex);
@@ -271,25 +253,18 @@
         ensureBytesUsedBelowLimit();
         StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
         StatsdStats::getInstance().setUidMapChanges(mChanges.size());
-        getListenerListCopyLocked(&broadcastList);
+        broadcast = mSubscriber;
     }
 
-    for (const auto& weakPtr : broadcastList) {
-        auto strongPtr = weakPtr.promote();
-        if (strongPtr != NULL) {
-            strongPtr->notifyAppRemoved(timestamp, app, uid);
-        }
+    auto strongPtr = broadcast.promote();
+    if (strongPtr != NULL) {
+        strongPtr->notifyAppRemoved(timestamp, app, uid);
     }
 }
 
-void UidMap::addListener(wp<PackageInfoListener> producer) {
+void UidMap::setListener(wp<PackageInfoListener> listener) {
     lock_guard<mutex> lock(mMutex);  // Lock for updates
-    mSubscribers.insert(producer);
-}
-
-void UidMap::removeListener(wp<PackageInfoListener> producer) {
-    lock_guard<mutex> lock(mMutex);  // Lock for updates
-    mSubscribers.erase(producer);
+    mSubscriber = listener;
 }
 
 void UidMap::assignIsolatedUid(int isolatedUid, int parentUid) {
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index a7c5fb2..2d3f6ee 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -118,12 +118,10 @@
     // adb shell cmd stats print-uid-map
     void printUidMap(int outFd) const;
 
-    // Commands for indicating to the map that a producer should be notified if an app is updated.
-    // This allows the metric producer to distinguish when the same uid or app represents a
-    // different version of an app.
-    void addListener(wp<PackageInfoListener> producer);
-    // Remove the listener from the set of metric producers that subscribe to updates.
-    void removeListener(wp<PackageInfoListener> producer);
+    // Command for indicating to the map that StatsLogProcessor should be notified if an app is
+    // updated. This allows metric producers and managers to distinguish when the same uid or app
+    // represents a different version of an app.
+    void setListener(wp<PackageInfoListener> listener);
 
     // Informs uid map that a config is added/updated. Used for keeping mConfigKeys up to date.
     void OnConfigUpdated(const ConfigKey& key);
@@ -167,8 +165,6 @@
     std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const;
     string normalizeAppName(const string& appName) const;
 
-    void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output);
-
     void writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings,
                                    bool includeInstaller, const std::set<int32_t>& interestingUids,
                                    std::set<string>* str_set, ProtoOutputStream* proto);
@@ -195,8 +191,8 @@
     // Store which uid and apps represent deleted ones.
     std::list<std::pair<int, string>> mDeletedApps;
 
-    // Metric producers that should be notified if there's an upgrade in any app.
-    set<wp<PackageInfoListener>> mSubscribers;
+    // Notify StatsLogProcessor if there's an upgrade/removal in any app.
+    wp<PackageInfoListener> mSubscriber;
 
     // Mapping of config keys we're aware of to the epoch time they last received an update. This
     // lets us know it's safe to delete events older than the oldest update. The value is nanosec.
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 309d251..0bc3ebb 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -162,7 +162,10 @@
 
     ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
     backfillStartEndTimestamp(&report);
-    EXPECT_EQ(1, report.metrics_size());
+
+    ASSERT_EQ(1, report.metrics_size());
+    ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
+    ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
     EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
                     has_start_bucket_elapsed_nanos());
     EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
@@ -186,7 +189,10 @@
 
     ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
     backfillStartEndTimestamp(&report);
-    EXPECT_EQ(1, report.metrics_size());
+
+    ASSERT_EQ(1, report.metrics_size());
+    ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
+    ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
     EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
                     has_start_bucket_elapsed_nanos());
     EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
@@ -228,8 +234,9 @@
     ConfigMetricsReport report =
             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
     backfillStartEndTimestamp(&report);
-    EXPECT_EQ(1, report.metrics_size());
-    EXPECT_EQ(1, report.metrics(0).value_metrics().skipped_size());
+
+    ASSERT_EQ(1, report.metrics_size());
+    ASSERT_EQ(1, report.metrics(0).value_metrics().skipped_size());
     EXPECT_TRUE(report.metrics(0).value_metrics().skipped(0).has_start_bucket_elapsed_nanos());
     // Can't test the start time since it will be based on the actual time when the pulling occurs.
     EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
@@ -270,8 +277,8 @@
     ConfigMetricsReport report =
             GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
     backfillStartEndTimestamp(&report);
-    EXPECT_EQ(1, report.metrics_size());
-    EXPECT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
+    ASSERT_EQ(1, report.metrics_size());
+    ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
     // Can't test the start time since it will be based on the actual time when the pulling occurs.
     EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos());
     EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
diff --git a/core/java/android/accessibilityservice/OWNERS b/core/java/android/accessibilityservice/OWNERS
index 265674a..c6f42f7 100644
--- a/core/java/android/accessibilityservice/OWNERS
+++ b/core/java/android/accessibilityservice/OWNERS
@@ -1,3 +1,4 @@
 svetoslavganov@google.com
 pweaver@google.com
 rhedjao@google.com
+qasid@google.com
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
index 229bee5..5bc9992 100644
--- a/core/java/android/app/DexLoadReporter.java
+++ b/core/java/android/app/DexLoadReporter.java
@@ -28,9 +28,8 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -87,50 +86,32 @@
     }
 
     @Override
-    public void report(List<ClassLoader> classLoadersChain, List<String> classPaths) {
-        if (classLoadersChain.size() != classPaths.size()) {
-            Slog.wtf(TAG, "Bad call to DexLoadReporter: argument size mismatch");
-            return;
-        }
-        if (classPaths.isEmpty()) {
-            Slog.wtf(TAG, "Bad call to DexLoadReporter: empty dex paths");
-            return;
-        }
-
-        // The first element of classPaths is the list of dex files that should be registered.
-        // The classpath is represented as a list of dex files separated by File.pathSeparator.
-        String[] dexPathsForRegistration = classPaths.get(0).split(File.pathSeparator);
-        if (dexPathsForRegistration.length == 0) {
-            // No dex files to register.
+    public void report(Map<String, String> classLoaderContextMap) {
+        if (classLoaderContextMap.isEmpty()) {
+            Slog.wtf(TAG, "Bad call to DexLoadReporter: empty classLoaderContextMap");
             return;
         }
 
         // Notify the package manager about the dex loads unconditionally.
         // The load might be for either a primary or secondary dex file.
-        notifyPackageManager(classLoadersChain, classPaths);
+        notifyPackageManager(classLoaderContextMap);
         // Check for secondary dex files and register them for profiling if possible.
         // Note that we only register the dex paths belonging to the first class loader.
-        registerSecondaryDexForProfiling(dexPathsForRegistration);
+        registerSecondaryDexForProfiling(classLoaderContextMap.keySet());
     }
 
-    private void notifyPackageManager(List<ClassLoader> classLoadersChain,
-            List<String> classPaths) {
+    private void notifyPackageManager(Map<String, String> classLoaderContextMap) {
         // Get the class loader names for the binder call.
-        List<String> classLoadersNames = new ArrayList<>(classPaths.size());
-        for (ClassLoader classLoader : classLoadersChain) {
-            classLoadersNames.add(classLoader.getClass().getName());
-        }
         String packageName = ActivityThread.currentPackageName();
         try {
-            ActivityThread.getPackageManager().notifyDexLoad(
-                    packageName, classLoadersNames, classPaths,
-                    VMRuntime.getRuntime().vmInstructionSet());
+            ActivityThread.getPackageManager().notifyDexLoad(packageName,
+                    classLoaderContextMap, VMRuntime.getRuntime().vmInstructionSet());
         } catch (RemoteException re) {
             Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
         }
     }
 
-    private void registerSecondaryDexForProfiling(String[] dexPaths) {
+    private void registerSecondaryDexForProfiling(Set<String> dexPaths) {
         if (!SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
             return;
         }
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
new file mode 100644
index 0000000..844e72e
--- /dev/null
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+import android.annotation.NonNull;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * LRU cache that's invalidated when an opaque value in a property changes. Self-synchronizing,
+ * but doesn't hold a lock across data fetches on query misses.
+ *
+ * The intended use case is caching frequently-read, seldom-changed information normally
+ * retrieved across interprocess communication. Imagine that you've written a user birthday
+ * information daemon called "birthdayd" that exposes an {@code IUserBirthdayService} interface
+ * over binder. That binder interface looks something like this:
+ *
+ * <pre>
+ * parcelable Birthday {
+ *   int month;
+ *   int day;
+ * }
+ * interface IUserBirthdayService {
+ *   Birthday getUserBirthday(int userId);
+ * }
+ * </pre>
+ *
+ * Suppose the service implementation itself looks like this...
+ *
+ * <pre>
+ * public class UserBirthdayServiceImpl implements IUserBirthdayService {
+ *   private final HashMap<Integer, Birthday> mUidToBirthday;
+ *   @Override
+ *   public synchronized Birthday getUserBirthday(int userId) {
+ *     return mUidToBirthday.get(userId);
+ *   }
+ *   private synchronized void updateBirthdays(Map<Integer, Birthday> uidToBirthday) {
+ *     mUidToBirthday.clear();
+ *     mUidToBirthday.putAll(uidToBirthday);
+ *   }
+ * }
+ * </pre>
+ *
+ * ... and we have a client in frameworks (loaded into every app process) that looks
+ * like this:
+ *
+ * <pre>
+ * public class ActivityThread {
+ *   ...
+ *   public Birthday getUserBirthday(int userId) {
+ *     return GetService("birthdayd").getUserBirthday(userId);
+ *   }
+ *   ...
+ * }
+ * </pre>
+ *
+ * With this code, every time an app calls {@code getUserBirthday(uid)}, we make a binder call
+ * to the birthdayd process and consult its database of birthdays. If we query user birthdays
+ * frequently, we do a lot of work that we don't have to do, since user birthdays
+ * change infrequently.
+ *
+ * PropertyInvalidatedCache is part of a pattern for optimizing this kind of
+ * information-querying code. Using {@code PropertyInvalidatedCache}, you'd write the client
+ * this way:
+ *
+ * <pre>
+ * public class ActivityThread {
+ *   ...
+ *   private static final int BDAY_CACHE_MAX = 8;  // Maximum birthdays to cache
+ *   private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
+ *   private final PropertyInvalidatedCache<Integer, Birthday> mBirthdayCache = new
+ *     PropertyInvalidatedCache<Integer, Birthday>(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
+ *       @Override
+ *       protected Birthday recompute(Integer userId) {
+ *         return GetService("birthdayd").getUserBirthday(userId);
+ *       }
+ *     };
+ *   public void disableUserBirthdayCache() {
+ *     mBirthdayCache.disableLocal();
+ *   }
+ *   public void invalidateUserBirthdayCache() {
+ *     mBirthdayCache.invalidateCache();
+ *   }
+ *   public Birthday getUserBirthday(int userId) {
+ *     return mBirthdayCache.query(userId);
+ *   }
+ *   ...
+ * }
+ * </pre>
+ *
+ * With this cache, clients perform a binder call to birthdayd if asking for a user's birthday
+ * for the first time; on subsequent queries, we return the already-known Birthday object.
+ *
+ * User birthdays do occasionally change, so we have to modify the server to invalidate this
+ * cache when necessary. That invalidation code looks like this:
+ *
+ * <pre>
+ * public class UserBirthdayServiceImpl {
+ *   ...
+ *   public UserBirthdayServiceImpl() {
+ *     ...
+ *     ActivityThread.currentActivityThread().disableUserBirthdayCache();
+ *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
+ *   }
+ *
+ *   private synchronized void updateBirthdays(Map<Integer, Birthday> uidToBirthday) {
+ *     mUidToBirthday.clear();
+ *     mUidToBirthday.putAll(uidToBirthday);
+ *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
+ *   }
+ *   ...
+ * }
+ * </pre>
+ *
+ * The call to {@code PropertyInvalidatedCache.invalidateCache()} guarantees that all clients
+ * will re-fetch birthdays from binder during consequent calls to
+ * {@code ActivityThread.getUserBirthday()}. Because the invalidate call happens with the lock
+ * held, we maintain consistency between different client views of the birthday state. The use
+ * of PropertyInvalidatedCache in this idiomatic way introduces no new race conditions.
+ *
+ * PropertyInvalidatedCache has a few other features for doing things like incremental
+ * enhancement of cached values and invalidation of multiple caches (that all share the same
+ * property key) at once.
+ *
+ * {@code BDAY_CACHE_KEY} is the name of a property that we set to an opaque unique value each
+ * time we update the cache. SELinux configuration must allow everyone to read this property
+ * and it must allow any process that needs to invalidate the cache (here, birthdayd) to write
+ * the property. (These properties conventionally begin with the "cache_key." prefix.)
+ *
+ * The {@code UserBirthdayServiceImpl} constructor calls {@code disableUserBirthdayCache()} so
+ * that calls to {@code getUserBirthday} from inside birthdayd don't go through the cache. In
+ * this local case, there's no IPC, so use of the cache is (depending on exact
+ * circumstance) unnecessary.
+ *
+ * @param <Query> The class used to index cache entries: must be hashable and comparable
+ * @param <Result> The class holding cache entries; use a boxed primitive if possible
+ *
+ * {@hide}
+ */
+public abstract class PropertyInvalidatedCache<Query, Result> {
+    private static final long NONCE_UNSET = 0;
+    private static final long NONCE_DISABLED = -1;
+
+    private static final String TAG = "PropertyInvalidatedCache";
+    private static final boolean DEBUG = false;
+    private static final boolean ENABLE = true;
+
+    private final Object mLock = new Object();
+
+    /**
+     * Name of the property that holds the unique value that we use to invalidate the cache.
+     */
+    private final String mPropertyName;
+
+    /**
+     * Handle to the {@code mPropertyName} property, transitioning to non-{@code null} once the
+     * property exists on the system.
+     */
+    private volatile SystemProperties.Handle mPropertyHandle;
+
+    @GuardedBy("mLock")
+    private final LinkedHashMap<Query, Result> mCache;
+
+    /**
+     * The last value of the {@code mPropertyHandle} that we observed.
+     */
+    @GuardedBy("mLock")
+    private long mLastSeenNonce = NONCE_UNSET;
+
+    /**
+     * Whether we've disabled the cache in this process.
+     */
+    private boolean mDisabled = false;
+
+    /**
+     * Make a new property invalidated cache.
+     *
+     * @param maxEntries Maximum number of entries to cache; LRU discard
+     * @param propertyName Name of the system property holding the cache invalidation nonce
+     */
+    public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) {
+        mPropertyName = propertyName;
+        mCache = new LinkedHashMap<Query, Result>(
+            2 /* start small */,
+            0.75f /* default load factor */,
+            true /* LRU access order */) {
+                @Override
+                protected boolean removeEldestEntry(Map.Entry eldest) {
+                    return size() > maxEntries;
+                }
+            };
+    }
+
+    /**
+     * Forget all cached values.
+     */
+    public final void clear() {
+        synchronized (mLock) {
+            mCache.clear();
+        }
+    }
+
+    /**
+     * Fetch a result from scratch in case it's not in the cache at all.  Called unlocked: may
+     * block. If this function returns null, the result of the cache query is null. There is no
+     * "negative cache" in the query: we don't cache null results at all.
+     */
+    protected abstract Result recompute(Query query);
+
+    /**
+     * Make result up-to-date on a cache hit.  Called unlocked;
+     * may block.
+     *
+     * Return either 1) oldResult itself (the same object, by reference equality), in which
+     * case we just return oldResult as the result of the cache query, 2) a new object, which
+     * replaces oldResult in the cache and which we return as the result of the cache query
+     * after performing another property read to make sure that the result hasn't changed in
+     * the meantime (if the nonce has changed in the meantime, we drop the cache and try the
+     * whole query again), or 3) null, which causes the old value to be removed from the cache
+     * and null to be returned as the result of the cache query.
+     */
+    protected Result refresh(Result oldResult, Query query) {
+        return oldResult;
+    }
+
+    private long getCurrentNonce() {
+        SystemProperties.Handle handle = mPropertyHandle;
+        if (handle == null) {
+            handle = SystemProperties.find(mPropertyName);
+            if (handle == null) {
+                return NONCE_UNSET;
+            }
+            mPropertyHandle = handle;
+        }
+        return handle.getLong(NONCE_UNSET);
+    }
+
+    /**
+     * Disable the use of this cache in this process.
+     */
+    public final void disableLocal() {
+        synchronized (mLock) {
+            mDisabled = true;
+            mCache.clear();
+        }
+    }
+
+    /**
+     * Return whether the cache is disabled in this process.
+     */
+    public final boolean isDisabledLocal() {
+        return mDisabled;
+    }
+
+    /**
+     * Get a value from the cache or recompute it.
+     */
+    public Result query(Query query) {
+        // Let access to mDisabled race: it's atomic anyway.
+        long currentNonce = (ENABLE && !mDisabled) ? getCurrentNonce() : NONCE_DISABLED;
+        for (;;) {
+            if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET) {
+                if (DEBUG) {
+                    Log.d(TAG,
+                            String.format("cache %s for %s",
+                                currentNonce == NONCE_DISABLED ? "disabled" : "unset",
+                                query));
+                }
+                return recompute(query);
+            }
+            final Result cachedResult;
+            synchronized (mLock) {
+                if (currentNonce == mLastSeenNonce) {
+                    cachedResult = mCache.get(query);
+                } else {
+                    if (DEBUG) {
+                        Log.d(TAG,
+                                String.format("clearing cache because nonce changed [%s] -> [%s]",
+                                        mLastSeenNonce, currentNonce));
+                    }
+                    mCache.clear();
+                    mLastSeenNonce = currentNonce;
+                    cachedResult = null;
+                }
+            }
+            // Cache hit --- but we're not quite done yet.  A value in the cache might need to
+            // be augmented in a "refresh" operation.  The refresh operation can combine the
+            // old and the new nonce values.  In order to make sure the new parts of the value
+            // are consistent with the old, possibly-reused parts, we check the property value
+            // again after the refresh and do the whole fetch again if the property invalidated
+            // us while we were refreshing.
+            if (cachedResult != null) {
+                final Result refreshedResult = refresh(cachedResult, query);
+                if (refreshedResult != cachedResult) {
+                    if (DEBUG) {
+                        Log.d(TAG, "cache refresh for " + query);
+                    }
+                    final long afterRefreshNonce = getCurrentNonce();
+                    if (currentNonce != afterRefreshNonce) {
+                        currentNonce = afterRefreshNonce;
+                        if (DEBUG) {
+                            Log.d(TAG, "restarting query because nonce changed in refresh");
+                        }
+                        continue;
+                    }
+                    synchronized (mLock) {
+                        if (currentNonce != mLastSeenNonce) {
+                            // Do nothing: cache is already out of date. Just return the value
+                            // we already have: there's no guarantee that the contents of mCache
+                            // won't become invalid as soon as we return.
+                        } else if (refreshedResult == null) {
+                            mCache.remove(query);
+                        } else {
+                            mCache.put(query, refreshedResult);
+                        }
+                    }
+                    return refreshedResult;
+                }
+                if (DEBUG) {
+                    Log.d(TAG, "cache hit for " + query);
+                }
+                return cachedResult;
+            }
+            // Cache miss: make the value from scratch.
+            if (DEBUG) {
+                Log.d(TAG, "cache miss for " + query);
+            }
+            final Result result = recompute(query);
+            synchronized (mLock) {
+                // If someone else invalidated the cache while we did the recomputation, don't
+                // update the cache with a potentially stale result.
+                if (mLastSeenNonce == currentNonce && result != null) {
+                    mCache.put(query, result);
+                }
+            }
+            return result;
+        }
+    }
+
+    // Inner class avoids initialization in processes that don't do any invalidation
+    private static final class NoPreloadHolder {
+        private static final AtomicLong sNextNonce = new AtomicLong((new Random()).nextLong());
+        public static long next() {
+            return sNextNonce.getAndIncrement();
+        }
+    }
+
+    /**
+     * Non-static convenience version of disableSystemWide() for situations in which only a
+     * single PropertyInvalidatedCache is keyed on a particular property value.
+     *
+     * When multiple caches share a single property value, using an instance method on one of
+     * the cache objects to invalidate all of the cache objects becomes confusing and you should
+     * just use the static version of this function.
+     */
+    public final void disableSystemWide() {
+        disableSystemWide(mPropertyName);
+    }
+
+    /**
+     * Disable all caches system-wide that are keyed on {@var name}. This
+     * function is synchronous: caches are invalidated and disabled upon return.
+     *
+     * @param name Name of the cache-key property to invalidate
+     */
+    public static void disableSystemWide(@NonNull String name) {
+        SystemProperties.set(name, Long.toString(NONCE_DISABLED));
+    }
+
+    /**
+     * Non-static convenience version of invalidateCache() for situations in which only a single
+     * PropertyInvalidatedCache is keyed on a particular property value.
+     */
+    public final void invalidateCache() {
+        invalidateCache(mPropertyName);
+    }
+
+    /**
+     * Invalidate PropertyInvalidatedCache caches in all processes that are keyed on
+     * {@var name}. This function is synchronous: caches are invalidated upon return.
+     *
+     * @param name Name of the cache-key property to invalidate
+     */
+    public static void invalidateCache(@NonNull String name) {
+        // There's no race here: we don't require that values strictly increase, but instead
+        // only that each is unique in a single runtime-restart session.
+        final long nonce = SystemProperties.getLong(name, NONCE_UNSET);
+        if (nonce == NONCE_DISABLED) {
+            if (DEBUG) {
+                Log.d(TAG, "refusing to invalidate disabled cache: " + name);
+            }
+            return;
+        }
+        long newValue;
+        do {
+            newValue = NoPreloadHolder.next();
+        } while (newValue == NONCE_UNSET || newValue == NONCE_DISABLED);
+        final String newValueString = Long.toString(newValue);
+        if (DEBUG) {
+            Log.d(TAG,
+                    String.format("invalidating cache [%s]: [%s] -> [%s]",
+                            name,
+                            nonce,
+                            newValueString));
+        }
+        SystemProperties.set(name, newValueString);
+    }
+}
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 72eea84..7e8a7de 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -625,10 +625,6 @@
         return hasLateConstraint;
     }
 
-    private static boolean kindofEqualsBundle(BaseBundle a, BaseBundle b) {
-        return (a == b) || (a != null && a.kindofEquals(b));
-    }
-
     @Override
     public boolean equals(Object o) {
         if (!(o instanceof JobInfo)) {
@@ -639,11 +635,11 @@
             return false;
         }
         // XXX won't be correct if one is parcelled and the other not.
-        if (!kindofEqualsBundle(extras, j.extras)) {
+        if (!BaseBundle.kindofEquals(extras, j.extras)) {
             return false;
         }
         // XXX won't be correct if one is parcelled and the other not.
-        if (!kindofEqualsBundle(transientExtras, j.transientExtras)) {
+        if (!BaseBundle.kindofEquals(transientExtras, j.transientExtras)) {
             return false;
         }
         // XXX for now we consider two different clip data objects to be different,
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 5b60b85..838a3b9 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1025,7 +1025,11 @@
         try {
             String name = service.getRemoteName(this);
             if (name != null) {
-                return name.replaceAll("[\\t\\n\\r]+", " ");
+                // remove whitespace characters from the name
+                return name
+                        .replace('\t', ' ')
+                        .replace('\n', ' ')
+                        .replace('\r', ' ');
             }
             return null;
         } catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 26e3e27..e9e1f68 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -18,7 +18,6 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -330,6 +329,7 @@
      * {@inheritDoc}
      */
     @Override
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public @NonNull List<BluetoothDevice> getConnectedDevices() {
         if (VDBG) log("getConnectedDevices()");
         final IBluetoothHidHost service = getService();
@@ -370,8 +370,12 @@
      * {@inheritDoc}
      */
     @Override
-    public int getConnectionState(@Nullable BluetoothDevice device) {
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public int getConnectionState(@NonNull BluetoothDevice device) {
         if (VDBG) log("getState(" + device + ")");
+        if (device == null) {
+            throw new IllegalArgumentException("device must not be null");
+        }
         final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
@@ -416,9 +420,12 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
+    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+        if (device == null) {
+            throw new IllegalArgumentException("device must not be null");
+        }
         final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
@@ -465,8 +472,11 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
+    public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
+        if (device == null) {
+            throw new IllegalArgumentException("device must not be null");
+        }
         final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
             try {
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 1c62faa..cc2b615 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -27,6 +27,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.CloseGuard;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -39,12 +40,14 @@
  * @hide
  */
 @SystemApi
-public final class BluetoothMap implements BluetoothProfile {
+public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
 
     private static final String TAG = "BluetoothMap";
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
+    private CloseGuard mCloseGuard;
+
     /** @hide */
     @SuppressLint("ActionValue")
     @SystemApi
@@ -86,15 +89,16 @@
         if (DBG) Log.d(TAG, "Create BluetoothMap proxy object");
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         mProfileConnector.connect(context, listener);
+        mCloseGuard = new CloseGuard();
+        mCloseGuard.open("close");
     }
 
-    @SuppressLint("GenericException")
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    protected void finalize() {
+        if (mCloseGuard != null) {
+            mCloseGuard.warnIfOpen();
         }
+        close();
     }
 
     /**
@@ -105,7 +109,10 @@
      *
      * @hide
      */
-    public synchronized void close() {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void close() {
+        if (VDBG) log("close()");
         mProfileConnector.disconnect();
     }
 
@@ -250,6 +257,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public @NonNull List<BluetoothDevice> getConnectedDevices() {
         if (DBG) log("getConnectedDevices()");
         final IBluetoothMap service = getService();
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 1f89ddf..277a5a8 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -278,16 +278,19 @@
     }
 
     /**
-     * Pbap does not store connection policy, so this function only disconnects pbap if
-     * connectionPolicy is {@link #CONNECTION_POLICY_FORBIDDEN}.
+     * Set connection policy of the profile and tries to disconnect it if connectionPolicy is
+     * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
      *
      * <p> The device should already be paired.
-     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
-     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
+     * Connection policy can be one of:
+     * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
+     * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
+     * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
      *
      * @param device Paired bluetooth device
-     * @param connectionPolicy determines whether to disconnect the device
-     * @return true if pbap is successfully disconnected, false otherwise
+     * @param connectionPolicy is the connection policy to set to for this profile
+     * @return true if connectionPolicy is set, false on error
+     *
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4527d8e..8794173 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3356,6 +3356,7 @@
             CONSUMER_IR_SERVICE,
             //@hide: TRUST_SERVICE,
             TV_INPUT_SERVICE,
+            //@hide: TV_TUNER_RESOURCE_MGR_SERVICE,
             //@hide: NETWORK_SCORE_SERVICE,
             USAGE_STATS_SERVICE,
             MEDIA_SESSION_SERVICE,
@@ -4586,6 +4587,17 @@
     public static final String TV_INPUT_SERVICE = "tv_input";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.media.tv.tunerresourcemanager.TunerResourceManager} for interacting with TV
+     * tuner resources on the device.
+     *
+     * @see #getSystemService(String)
+     * @see android.media.tv.TunerResourceManager
+     * @hide
+     */
+    public static final String TV_TUNER_RESOURCE_MGR_SERVICE = "tv_tuner_resource_mgr";
+
+    /**
      * {@link android.net.NetworkScoreManager} for managing network scoring.
      * @see #getSystemService(String)
      * @see android.net.NetworkScoreManager
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d859a3af..99ca048 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4515,12 +4515,6 @@
     public static final String EXTRA_LTE_EARFCN_RSRP_BOOST = "LteEarfcnRsrpBoost";
 
     /**
-     * An parcelable extra used with {@link #ACTION_SERVICE_STATE} representing the service state.
-     * @hide
-     */
-    public static final String EXTRA_SERVICE_STATE = "android.intent.extra.SERVICE_STATE";
-
-    /**
      * The name of the extra used to define the text to be processed, as a
      * CharSequence. Note that this may be a styled CharSequence, so you must use
      * {@link Bundle#getCharSequence(String) Bundle.getCharSequence()} to retrieve it.
@@ -6632,7 +6626,7 @@
                     this.mClipData = new ClipData(o.mClipData);
                 }
             } else {
-                if (o.mExtras != null && !o.mExtras.maybeIsEmpty()) {
+                if (o.mExtras != null && !o.mExtras.isDefinitelyEmpty()) {
                     this.mExtras = Bundle.STRIPPED;
                 }
 
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 429a6e5..6d051e4 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -529,19 +529,12 @@
      * Notify the package manager that a list of dex files have been loaded.
      *
      * @param loadingPackageName the name of the package who performs the load
-     * @param classLoadersNames the names of the class loaders present in the loading chain. The
-     *    list encodes the class loader chain in the natural order. The first class loader has
-     *    the second one as its parent and so on. The dex files present in the class path of the
-     *    first class loader will be recorded in the usage file.
-     * @param classPaths the class paths corresponding to the class loaders names from
-     *     {@param classLoadersNames}. The the first element corresponds to the first class loader
-     *     and so on. A classpath is represented as a list of dex files separated by
-     *     {@code File.pathSeparator}, or null if the class loader's classpath is not known.
-     *     The dex files found in the first class path will be recorded in the usage file.
+     * @param classLoaderContextMap a map from file paths to dex files that have been loaded to
+     *     the class loader context that was used to load them.
      * @param loaderIsa the ISA of the loader process
      */
-    oneway void notifyDexLoad(String loadingPackageName, in List<String> classLoadersNames,
-            in List<String> classPaths, String loaderIsa);
+    oneway void notifyDexLoad(String loadingPackageName,
+            in Map<String, String> classLoaderContextMap, String loaderIsa);
 
     /**
      * Register an application dex module with the package manager.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 93b90b4..2db661d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2147,6 +2147,23 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature(String, int)}: If this feature is supported, the feature version
+     * specifies a date such that the device is known to pass the Vulkan dEQP test suite associated
+     * with that date.  The date is encoded as follows:
+     * <ul>
+     * <li>Year in bits 31-16</li>
+     * <li>Month in bits 15-8</li>
+     * <li>Day in bits 7-0</li>
+     * </ul>
+     * <p>
+     * Example: 2019-03-01 is encoded as 0x07E30301, and would indicate that the device passes the
+     * Vulkan dEQP test suite version that was current on 2019-03-01.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_VULKAN_DEQP_LEVEL = "android.software.vulkan.deqp.level";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device includes broadcast radio tuner.
      * @hide
      */
@@ -2867,6 +2884,13 @@
     public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device does not have a slices implementation.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SLICES_DISABLED = "android.software.slices_disabled";
+    /**
      * Extra field name for the URI to a verification file. Passed to a package
      * verifier.
      *
diff --git a/core/java/android/debug/AdbManager.java b/core/java/android/debug/AdbManager.java
index 0a76bed..7714dd8 100644
--- a/core/java/android/debug/AdbManager.java
+++ b/core/java/android/debug/AdbManager.java
@@ -31,6 +31,114 @@
 public class AdbManager {
     private static final String TAG = "AdbManager";
 
+    /**
+     * Action indicating the state change of wireless debugging. Can be either
+     *   STATUS_CONNECTED
+     *   STATUS_DISCONNECTED
+     *
+     * @hide
+     */
+    public static final String WIRELESS_DEBUG_STATE_CHANGED_ACTION =
+            "com.android.server.adb.WIRELESS_DEBUG_STATUS";
+
+    /**
+     * Contains the list of paired devices.
+     *
+     * @hide
+     */
+    public static final String WIRELESS_DEBUG_PAIRED_DEVICES_ACTION =
+            "com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES";
+
+    /**
+     * Action indicating the status of a pairing. Can be either
+     *   WIRELESS_STATUS_FAIL
+     *   WIRELESS_STATUS_SUCCESS
+     *   WIRELESS_STATUS_CANCELLED
+     *   WIRELESS_STATUS_PAIRING_CODE
+     *   WIRELESS_STATUS_CONNECTED
+     *
+     * @hide
+     */
+    public static final String WIRELESS_DEBUG_PAIRING_RESULT_ACTION =
+            "com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT";
+
+    /**
+     * Extra containing the PairDevice map of paired/pairing devices.
+     *
+     * @hide
+     */
+    public static final String WIRELESS_DEVICES_EXTRA = "devices_map";
+
+    /**
+     * The status of the pairing/unpairing.
+     *
+     * @hide
+     */
+    public static final String WIRELESS_STATUS_EXTRA = "status";
+
+    /**
+     * The PairDevice.
+     *
+     * @hide
+     */
+    public static final String WIRELESS_PAIR_DEVICE_EXTRA = "pair_device";
+
+    /**
+     * The six-digit pairing code.
+     *
+     * @hide
+     */
+    public static final String WIRELESS_PAIRING_CODE_EXTRA = "pairing_code";
+
+    /**
+     * The adb connection/pairing port that was opened.
+     *
+     * @hide
+     */
+    public static final String WIRELESS_DEBUG_PORT_EXTRA = "adb_port";
+
+    /**
+     * Status indicating the pairing/unpairing failed.
+     *
+     * @hide
+     */
+    public static final int WIRELESS_STATUS_FAIL = 0;
+
+    /**
+     * Status indicating the pairing/unpairing succeeded.
+     *
+     * @hide
+     */
+    public static final int WIRELESS_STATUS_SUCCESS = 1;
+
+    /**
+     * Status indicating the pairing/unpairing was cancelled.
+     *
+     * @hide
+     */
+    public static final int WIRELESS_STATUS_CANCELLED = 2;
+
+    /**
+     * Status indicating the pairing code for pairing.
+     *
+     * @hide
+     */
+    public static final int WIRELESS_STATUS_PAIRING_CODE = 3;
+
+    /**
+     * Status indicating wireless debugging is connected.
+     *
+     * @hide
+     */
+    public static final int WIRELESS_STATUS_CONNECTED = 4;
+
+    /**
+     * Status indicating wireless debugging is disconnected.
+     *
+     * @hide
+     */
+    public static final int WIRELESS_STATUS_DISCONNECTED = 5;
+
     private final Context mContext;
     private final IAdbManager mService;
 
diff --git a/core/java/android/debug/AdbManagerInternal.java b/core/java/android/debug/AdbManagerInternal.java
index 51eb7fc..d730129 100644
--- a/core/java/android/debug/AdbManagerInternal.java
+++ b/core/java/android/debug/AdbManagerInternal.java
@@ -42,7 +42,7 @@
     /**
      * Returns {@code true} if ADB debugging is enabled.
      */
-    public abstract boolean isAdbEnabled();
+    public abstract boolean isAdbEnabled(byte transportType);
 
     /**
      * Returns the file that contains all of the ADB keys used by the device.
@@ -53,4 +53,14 @@
      * Returns the file that contains all of the ADB keys and their last used time.
      */
     public abstract File getAdbTempKeysFile();
+
+    /**
+     * Starts adbd for a transport.
+     */
+    public abstract void startAdbdForTransport(byte transportType);
+
+    /**
+     * Stops adbd for a transport.
+     */
+    public abstract void stopAdbdForTransport(byte transportType);
 }
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl b/core/java/android/debug/AdbTransportType.aidl
similarity index 70%
rename from telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl
rename to core/java/android/debug/AdbTransportType.aidl
index e9cbd9c..6904615 100644
--- a/telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl
+++ b/core/java/android/debug/AdbTransportType.aidl
@@ -1,12 +1,11 @@
 /*
- *
- * Copyright 2019, The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -15,6 +14,12 @@
  * limitations under the License.
  */
 
-package android.telephony.ims;
+package android.debug;
 
-parcelable RcsMessageQueryParams;
+/** @hide */
+@Backing(type="byte")
+enum AdbTransportType {
+    USB,
+    WIFI,
+}
+
diff --git a/core/java/android/debug/IAdbManager.aidl b/core/java/android/debug/IAdbManager.aidl
index c48fc07..aea7633 100644
--- a/core/java/android/debug/IAdbManager.aidl
+++ b/core/java/android/debug/IAdbManager.aidl
@@ -43,6 +43,62 @@
     void clearDebuggingKeys();
 
     /**
+     * Allow ADB wireless debugging on the connected network. If {@code alwaysAllow}
+     * is {@code true}, add {@code bssid} to list of networks that the user has
+     * approved.
+     *
+     * @param alwaysAllow if true, add permanently to list of allowed networks
+     * @param bssid BSSID of the network
+     */
+    void allowWirelessDebugging(boolean alwaysAllow, String bssid);
+
+    /**
+     * Deny ADB wireless debugging on the connected network.
+     */
+    void denyWirelessDebugging();
+
+    /**
+     * Returns a Map<String, PairDevice> with the key fingerprint mapped to the device information.
+     */
+    Map getPairedDevices();
+
+    /**
+     * Unpair the device identified by the key fingerprint it uses.
+     *
+     * @param fingerprint fingerprint of the key the device is using.
+     */
+    void unpairDevice(String fingerprint);
+
+    /**
+     * Enables pairing by pairing code. The result of the enable will be sent via intent action
+     * {@link android.debug.AdbManager#WIRELESS_DEBUG_ENABLE_DISCOVER_ACTION}. Furthermore, the
+     * pairing code will also be sent in the intent as an extra
+     * @{link android.debug.AdbManager#WIRELESS_PAIRING_CODE_EXTRA}. Note that only one
+     * pairing method can be enabled at a time, either by pairing code, or by QR code.
+     */
+    void enablePairingByPairingCode();
+
+    /**
+     * Enables pairing by QR code. The result of the enable will be sent via intent action
+     * {@link android.debug.AdbManager#WIRELESS_DEBUG_ENABLE_DISCOVER_ACTION}. Note that only one
+     * pairing method can be enabled at a time, either by pairing code, or by QR code.
+     *
+     * @param serviceName The MDNS service name parsed from the QR code.
+     * @param password The password parsed from the QR code.
+     */
+    void enablePairingByQrCode(String serviceName, String password);
+
+    /**
+     * Returns the network port that adb wireless server is running on.
+     */
+    int getAdbWirelessPort();
+
+    /**
+     * Disables pairing.
+     */
+    void disablePairing();
+
+    /**
      * Returns true if device supports secure Adb over Wi-Fi.
      */
     boolean isAdbWifiSupported();
diff --git a/core/java/android/debug/IAdbTransport.aidl b/core/java/android/debug/IAdbTransport.aidl
index 77211fc93..f018813 100644
--- a/core/java/android/debug/IAdbTransport.aidl
+++ b/core/java/android/debug/IAdbTransport.aidl
@@ -16,7 +16,9 @@
 
 package android.debug;
 
+import android.debug.AdbTransportType;
+
 /** @hide */
 interface IAdbTransport {
-    void onAdbEnabled(boolean enabled);
+    void onAdbEnabled(boolean enabled, in AdbTransportType type);
 }
diff --git a/core/java/android/debug/PairDevice.java b/core/java/android/debug/PairDevice.java
new file mode 100644
index 0000000..2d5b446
--- /dev/null
+++ b/core/java/android/debug/PairDevice.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 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.debug;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.Immutable;
+import com.android.internal.util.Preconditions;
+
+/**
+ * Contains information about the client in an ADB connection.
+ * @hide
+ */
+@Immutable
+public class PairDevice implements Parcelable {
+    /**
+     * The human-readable name of the device.
+     */
+    @NonNull private final String mName;
+
+    /**
+     * The device's guid.
+     */
+    @NonNull private final String mGuid;
+
+    /**
+     * Indicates whether the device is currently connected to adbd.
+     */
+    private final boolean mConnected;
+
+    public PairDevice(@NonNull String name, @NonNull String guid, boolean connected) {
+        Preconditions.checkStringNotEmpty(name);
+        Preconditions.checkStringNotEmpty(guid);
+        mName = name;
+        mGuid = guid;
+        mConnected = connected;
+    }
+
+    /**
+     * @return the device name.
+     */
+    @NonNull
+    public String getDeviceName() {
+        return mName;
+    }
+
+    /**
+     * @return the device GUID.
+     */
+    @NonNull
+    public String getGuid() {
+        return mGuid;
+    }
+
+    /**
+     * @return the adb connection state of the device.
+     */
+    public boolean isConnected() {
+        return mConnected;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mName);
+        dest.writeString(mGuid);
+        dest.writeBoolean(mConnected);
+    }
+
+    /**
+     * @return Human-readable info about the object.
+     */
+    @Override
+    public String toString() {
+        return "\n" + mName + "\n" + mGuid + "\n" + mConnected;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<PairDevice> CREATOR =
+            new Creator<PairDevice>() {
+                @Override
+                public PairDevice createFromParcel(Parcel source) {
+                    return new PairDevice(source.readString(), source.readString(),
+                            source.readBoolean());
+                }
+
+                @Override
+                public PairDevice[] newArray(int size) {
+                    return new PairDevice[size];
+                }
+            };
+}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index b0d0b4c..f540bfb 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -18,6 +18,7 @@
 package android.hardware.usb;
 
 import android.Manifest;
+import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
@@ -337,12 +338,14 @@
      * Code for the mtp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
      * {@hide}
      */
+    @SystemApi
     public static final long FUNCTION_MTP = GadgetFunction.MTP;
 
     /**
      * Code for the ptp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
      * {@hide}
      */
+    @SystemApi
     public static final long FUNCTION_PTP = GadgetFunction.PTP;
 
     /**
@@ -356,24 +359,28 @@
      * Code for the midi usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
      * {@hide}
      */
+    @SystemApi
     public static final long FUNCTION_MIDI = GadgetFunction.MIDI;
 
     /**
      * Code for the accessory usb function.
      * {@hide}
      */
+    @SystemApi
     public static final long FUNCTION_ACCESSORY = GadgetFunction.ACCESSORY;
 
     /**
      * Code for the audio source usb function.
      * {@hide}
      */
+    @SystemApi
     public static final long FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE;
 
     /**
      * Code for the adb usb function.
      * {@hide}
      */
+    @SystemApi
     public static final long FUNCTION_ADB = GadgetFunction.ADB;
 
     /**
@@ -399,6 +406,20 @@
         FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_NCM, FUNCTION_NCM);
     }
 
+    /** @hide */
+    @LongDef(flag = true, prefix = { "FUNCTION_" }, value = {
+            FUNCTION_NONE,
+            FUNCTION_MTP,
+            FUNCTION_PTP,
+            FUNCTION_RNDIS,
+            FUNCTION_MIDI,
+            FUNCTION_ACCESSORY,
+            FUNCTION_AUDIO_SOURCE,
+            FUNCTION_ADB,
+            FUNCTION_NCM,
+    })
+    public @interface UsbFunctionMode {}
+
     private final Context mContext;
     private final IUsbManager mService;
 
@@ -721,7 +742,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.MANAGE_USB)
-    public void setCurrentFunctions(long functions) {
+    public void setCurrentFunctions(@UsbFunctionMode long functions) {
         try {
             mService.setCurrentFunctions(functions);
         } catch (RemoteException e) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index cb31404..589b1aa 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1279,7 +1279,8 @@
     @UnsupportedAppUsage
     public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
         try {
-            return mService.getDefaultNetworkCapabilitiesForUser(userId);
+            return mService.getDefaultNetworkCapabilitiesForUser(
+                    userId, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1361,7 +1362,7 @@
     @Nullable
     public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
         try {
-            return mService.getNetworkCapabilities(network);
+            return mService.getNetworkCapabilities(network, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2424,14 +2425,14 @@
     /**
      * Get the set of tethered dhcp ranges.
      *
-     * @return an array of 0 or more {@code String} of tethered dhcp ranges.
-     * @deprecated This API just return the default value which is not used in DhcpServer.
+     * @deprecated This method is not supported.
+     * TODO: remove this function when all of clients are removed.
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     @Deprecated
     public String[] getTetheredDhcpRanges() {
-        return getTetheringManager().getTetheredDhcpRanges();
+        throw new UnsupportedOperationException("getTetheredDhcpRanges is not supported");
     }
 
     /**
@@ -4035,10 +4036,9 @@
             @NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
-        final String callingPackageName = mContext.getOpPackageName();
         try {
             mService.pendingRequestForNetwork(
-                    request.networkCapabilities, operation, callingPackageName);
+                    request.networkCapabilities, operation, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -4150,10 +4150,9 @@
             @NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
-        final String callingPackageName = mContext.getOpPackageName();
         try {
             mService.pendingListenForNetwork(
-                    request.networkCapabilities, operation, callingPackageName);
+                    request.networkCapabilities, operation, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -4716,19 +4715,19 @@
     /**
      * Returns the {@code uid} of the owner of a network connection.
      *
-     * @param protocol The protocol of the connection. Only {@code IPPROTO_TCP} and
-     * {@code IPPROTO_UDP} currently supported.
+     * @param protocol The protocol of the connection. Only {@code IPPROTO_TCP} and {@code
+     *     IPPROTO_UDP} currently supported.
      * @param local The local {@link InetSocketAddress} of a connection.
      * @param remote The remote {@link InetSocketAddress} of a connection.
-     *
      * @return {@code uid} if the connection is found and the app has permission to observe it
-     * (e.g., if it is associated with the calling VPN app's tunnel) or
-     * {@link android.os.Process#INVALID_UID} if the connection is not found.
-     * Throws {@link SecurityException} if the caller is not the active VPN for the current user.
-     * Throws {@link IllegalArgumentException} if an unsupported protocol is requested.
+     *     (e.g., if it is associated with the calling VPN app's VpnService tunnel) or {@link
+     *     android.os.Process#INVALID_UID} if the connection is not found.
+     * @throws {@link SecurityException} if the caller is not the active VpnService for the current
+     *     user.
+     * @throws {@link IllegalArgumentException} if an unsupported protocol is requested.
      */
-    public int getConnectionOwnerUid(int protocol, @NonNull InetSocketAddress local,
-            @NonNull InetSocketAddress remote) {
+    public int getConnectionOwnerUid(
+            int protocol, @NonNull InetSocketAddress local, @NonNull InetSocketAddress remote) {
         ConnectionInfo connectionInfo = new ConnectionInfo(protocol, local, remote);
         try {
             return mService.getConnectionOwnerUid(connectionInfo);
diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index a3899b7..139f5be 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -28,6 +28,7 @@
 
 import java.util.ArrayList;
 import java.util.Objects;
+import java.util.concurrent.Executor;
 
 /**
  * A class representing the IP configuration of the Ethernet network.
@@ -248,18 +249,19 @@
      * @param callback A callback to be called once the request has been fulfilled.
      */
     @NonNull
-    public TetheredInterfaceRequest requestTetheredInterface(
-            @NonNull TetheredInterfaceCallback callback) {
+    public TetheredInterfaceRequest requestTetheredInterface(@NonNull final Executor executor,
+            @NonNull final TetheredInterfaceCallback callback) {
         Objects.requireNonNull(callback, "Callback must be non-null");
+        Objects.requireNonNull(executor, "Executor must be non-null");
         final ITetheredInterfaceCallback cbInternal = new ITetheredInterfaceCallback.Stub() {
             @Override
             public void onAvailable(String iface) {
-                callback.onAvailable(iface);
+                executor.execute(() -> callback.onAvailable(iface));
             }
 
             @Override
             public void onUnavailable() {
-                callback.onUnavailable();
+                executor.execute(() -> callback.onUnavailable());
             }
         };
 
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 3a55461..1434560 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -59,7 +59,8 @@
     NetworkInfo[] getAllNetworkInfo();
     Network getNetworkForType(int networkType);
     Network[] getAllNetworks();
-    NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId);
+    NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
+            int userId, String callingPackageName);
 
     boolean isNetworkSupported(int networkType);
 
@@ -68,7 +69,7 @@
     LinkProperties getLinkPropertiesForType(int networkType);
     LinkProperties getLinkProperties(in Network network);
 
-    NetworkCapabilities getNetworkCapabilities(in Network network);
+    NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName);
 
     @UnsupportedAppUsage
     NetworkState[] getAllNetworkState();
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 42b4da1..f19a341 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -25,7 +25,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Process;
 import android.security.Credentials;
+import android.security.KeyStore;
+import android.security.keystore.AndroidKeyStoreProvider;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.net.VpnProfile;
@@ -59,6 +62,11 @@
  *     Exchange, Version 2 (IKEv2)</a>
  */
 public final class Ikev2VpnProfile extends PlatformVpnProfile {
+    /** Prefix for when a Private Key is an alias to look for in KeyStore @hide */
+    public static final String PREFIX_KEYSTORE_ALIAS = "KEYSTORE_ALIAS:";
+    /** Prefix for when a Private Key is stored directly in the profile @hide */
+    public static final String PREFIX_INLINE = "INLINE:";
+
     private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s";
     private static final String EMPTY_CERT = "";
 
@@ -339,7 +347,8 @@
                 break;
             case TYPE_IKEV2_IPSEC_RSA:
                 profile.ipsecUserCert = certificateToPemString(mUserCert);
-                profile.ipsecSecret = encodeForIpsecSecret(mRsaPrivateKey.getEncoded());
+                profile.ipsecSecret =
+                        PREFIX_INLINE + encodeForIpsecSecret(mRsaPrivateKey.getEncoded());
                 profile.ipsecCaCert =
                         mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
                 break;
@@ -360,6 +369,22 @@
     @NonNull
     public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
             throws IOException, GeneralSecurityException {
+        return fromVpnProfile(profile, null);
+    }
+
+    /**
+     * Builds the Ikev2VpnProfile from the given profile.
+     *
+     * @param profile the source VpnProfile to build from
+     * @param keyStore the Android Keystore instance to use to retrieve the private key, or null if
+     *     the private key is PEM-encoded into the profile.
+     * @return The IKEv2/IPsec VPN profile
+     * @hide
+     */
+    @NonNull
+    public static Ikev2VpnProfile fromVpnProfile(
+            @NonNull VpnProfile profile, @Nullable KeyStore keyStore)
+            throws IOException, GeneralSecurityException {
         final Builder builder = new Builder(profile.server, profile.ipsecIdentifier);
         builder.setProxy(profile.proxy);
         builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
@@ -378,8 +403,21 @@
                 builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret));
                 break;
             case TYPE_IKEV2_IPSEC_RSA:
+                final PrivateKey key;
+                if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) {
+                    Objects.requireNonNull(keyStore, "Missing Keystore for aliased PrivateKey");
+
+                    final String alias =
+                            profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length());
+                    key = AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
+                            keyStore, alias, Process.myUid());
+                } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) {
+                    key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length()));
+                } else {
+                    throw new IllegalArgumentException("Invalid RSA private key prefix");
+                }
+
                 final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert);
-                final PrivateKey key = getPrivateKey(profile.ipsecSecret);
                 final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert);
                 builder.setAuthDigitalSignature(userCert, key, serverRootCa);
                 break;
@@ -391,6 +429,39 @@
     }
 
     /**
+     * Validates that the VpnProfile is acceptable for the purposes of an Ikev2VpnProfile.
+     *
+     * @hide
+     */
+    public static boolean isValidVpnProfile(@NonNull VpnProfile profile) {
+        if (profile.server.isEmpty() || profile.ipsecIdentifier.isEmpty()) {
+            return false;
+        }
+
+        switch (profile.type) {
+            case TYPE_IKEV2_IPSEC_USER_PASS:
+                if (profile.username.isEmpty() || profile.password.isEmpty()) {
+                    return false;
+                }
+                break;
+            case TYPE_IKEV2_IPSEC_PSK:
+                if (profile.ipsecSecret.isEmpty()) {
+                    return false;
+                }
+                break;
+            case TYPE_IKEV2_IPSEC_RSA:
+                if (profile.ipsecSecret.isEmpty() || profile.ipsecUserCert.isEmpty()) {
+                    return false;
+                }
+                break;
+            default:
+                return false;
+        }
+
+        return true;
+    }
+
+    /**
      * Converts a X509 Certificate to a PEM-formatted string.
      *
      * <p>Must be public due to runtime-package restrictions.
@@ -432,7 +503,6 @@
 
     /** @hide */
     @NonNull
-    @VisibleForTesting(visibility = Visibility.PRIVATE)
     public static String encodeForIpsecSecret(@NonNull byte[] secret) {
         checkNotNull(secret, MISSING_PARAM_MSG_TMPL, "secret");
 
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 61a1484..c063b39 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -33,6 +33,7 @@
 import com.android.internal.util.Protocol;
 
 import java.util.ArrayList;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -53,6 +54,12 @@
     @NonNull
     public final Network network;
 
+    // Whether this NetworkAgent is using the legacy (never unhidden) API. The difference is
+    // that the legacy API uses NetworkInfo to convey the state, while the current API is
+    // exposing methods to manage it and generate it internally instead.
+    // TODO : remove this as soon as all agents have been converted.
+    private final boolean mIsLegacy;
+
     private final Handler mHandler;
     private volatile AsyncChannel mAsyncChannel;
     private final String LOG_TAG;
@@ -64,6 +71,10 @@
     private static final long BW_REFRESH_MIN_WIN_MS = 500;
     private boolean mBandwidthUpdateScheduled = false;
     private AtomicBoolean mBandwidthUpdatePending = new AtomicBoolean(false);
+    // Not used by legacy agents. Non-legacy agents use this to convert the NetworkAgent system API
+    // into the internal API of ConnectivityService.
+    @NonNull
+    private NetworkInfo mNetworkInfo;
 
     /**
      * The ID of the {@link NetworkProvider} that created this object, or
@@ -284,13 +295,16 @@
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
             int providerId) {
-        this(looper, context, logTag, nc, lp, score, config, providerId, ni);
+        this(looper, context, logTag, nc, lp, score, config, providerId, ni, true /* legacy */);
     }
 
     private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
         // The subtype can be changed with (TODO) setLegacySubtype, but it starts
         // with the type and an empty description.
-        return new NetworkInfo(config.legacyType, config.legacyType, config.legacyTypeName, "");
+        final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacyType,
+                config.legacyTypeName, "");
+        ni.setIsAvailable(true);
+        return ni;
     }
 
     /**
@@ -310,15 +324,17 @@
             @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
         this(looper, context, logTag, nc, lp, score, config,
                 provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(),
-                getLegacyNetworkInfo(config));
+                getLegacyNetworkInfo(config), false /* legacy */);
     }
 
     private NetworkAgent(Looper looper, Context context, String logTag, NetworkCapabilities nc,
             LinkProperties lp, int score, NetworkAgentConfig config, int providerId,
-            NetworkInfo ni) {
+            NetworkInfo ni, boolean legacy) {
         mHandler = new NetworkAgentHandler(looper);
         LOG_TAG = logTag;
         mContext = context;
+        mIsLegacy = legacy;
+        mNetworkInfo = new NetworkInfo(ni);
         this.providerId = providerId;
         if (ni == null || nc == null || lp == null) {
             throw new IllegalArgumentException();
@@ -483,15 +499,89 @@
      * @param linkProperties the new LinkProperties.
      */
     public void sendLinkProperties(@NonNull LinkProperties linkProperties) {
+        Objects.requireNonNull(linkProperties);
         queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
     }
 
     /**
+     * Inform ConnectivityService that this agent has now connected.
+     */
+    public void setConnected() {
+        if (mIsLegacy) {
+            throw new UnsupportedOperationException(
+                    "Legacy agents can't call setConnected.");
+        }
+        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+        queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
+    }
+
+    /**
+     * Unregister this network agent.
+     *
+     * This signals the network has disconnected and ends its lifecycle. After this is called,
+     * the network is torn down and this agent can no longer be used.
+     */
+    public void unregister() {
+        if (mIsLegacy) {
+            throw new UnsupportedOperationException(
+                    "Legacy agents can't call unregister.");
+        }
+        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+        queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
+    }
+
+    /**
+     * Change the legacy subtype of this network agent.
+     *
+     * This is only for backward compatibility and should not be used by non-legacy network agents,
+     * or agents that did not use to set a subtype. As such, only TYPE_MOBILE type agents can use
+     * this and others will be thrown an exception if they try.
+     *
+     * @deprecated this is for backward compatibility only.
+     * @param legacySubtype the legacy subtype.
+     */
+    @Deprecated
+    public void setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName) {
+        if (mIsLegacy) {
+            throw new UnsupportedOperationException("Legacy agents can't call setLegacySubtype.");
+        }
+        mNetworkInfo.setSubtype(legacySubtype, legacySubtypeName);
+        queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
+    }
+
+    /**
+     * Set the ExtraInfo of this network agent.
+     *
+     * This sets the ExtraInfo field inside the NetworkInfo returned by legacy public API and the
+     * broadcasts about the corresponding Network.
+     * This is only for backward compatibility and should not be used by non-legacy network agents,
+     * who will be thrown an exception if they try. The extra info should only be :
+     * <ul>
+     *   <li>For cellular agents, the APN name.</li>
+     *   <li>For ethernet agents, the interface name.</li>
+     * </ul>
+     *
+     * @deprecated this is for backward compatibility only.
+     * @param extraInfo the ExtraInfo.
+     */
+    @Deprecated
+    public void setLegacyExtraInfo(@Nullable final String extraInfo) {
+        if (mIsLegacy) {
+            throw new UnsupportedOperationException("Legacy agents can't call setLegacyExtraInfo.");
+        }
+        mNetworkInfo.setExtraInfo(extraInfo);
+        queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
+    }
+
+    /**
      * Must be called by the agent when it has a new NetworkInfo object.
      * @hide TODO: expose something better.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public void sendNetworkInfo(NetworkInfo networkInfo) {
+        if (!mIsLegacy) {
+            throw new UnsupportedOperationException("Only legacy agents can call sendNetworkInfo.");
+        }
         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
     }
 
@@ -500,6 +590,7 @@
      * @param networkCapabilities the new NetworkCapabilities.
      */
     public void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+        Objects.requireNonNull(networkCapabilities);
         mBandwidthUpdatePending.set(false);
         mLastBwRefreshTime = System.currentTimeMillis();
         queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index ef4a9e5..873d6e914 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -830,6 +830,23 @@
      * <p>This field keeps track of the UID of the app that created this network and is in charge of
      * its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running
      * VPN, or Carrier Service app managing a cellular data connection.
+     *
+     * <p>For NetworkCapability instances being sent from ConnectivityService, this value MUST be
+     * reset to Process.INVALID_UID unless all the following conditions are met:
+     *
+     * <ol>
+     *   <li>The destination app is the network owner
+     *   <li>The destination app has the ACCESS_FINE_LOCATION permission granted
+     *   <li>The user's location toggle is on
+     * </ol>
+     *
+     * This is because the owner UID is location-sensitive. The apps that request a network could
+     * know where the device is if they can tell for sure the system has connected to the network
+     * they requested.
+     *
+     * <p>This is populated by the network agents and for the NetworkCapabilities instance sent by
+     * an app to the System Server, the value MUST be reset to Process.INVALID_UID by the system
+     * server.
      */
     private int mOwnerUid = Process.INVALID_UID;
 
@@ -842,7 +859,16 @@
     }
 
     /**
-     * Retrieves the UID of the owner app.
+     * Retrieves the UID of the app that owns this network.
+     *
+     * <p>For user privacy reasons, this field will only be populated if:
+     *
+     * <ol>
+     *   <li>The calling app is the network owner
+     *   <li>The calling app has the ACCESS_FINE_LOCATION permission granted
+     *   <li>The user's location toggle is on
+     * </ol>
+     *
      */
     public int getOwnerUid() {
         return mOwnerUid;
@@ -880,8 +906,9 @@
      * @param administratorUids the UIDs to be set as administrators of this Network.
      * @hide
      */
+    @NonNull
     @SystemApi
-    public @NonNull NetworkCapabilities setAdministratorUids(
+    public NetworkCapabilities setAdministratorUids(
             @NonNull final List<Integer> administratorUids) {
         mAdministratorUids.clear();
         mAdministratorUids.addAll(administratorUids);
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index f19ba0f..2041cfb 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -126,7 +126,11 @@
         return getIntentForConfirmation();
     }
 
-    /** Delete the VPN profile configuration that was provisioned by the calling app */
+    /**
+     * Delete the VPN profile configuration that was provisioned by the calling app
+     *
+     * @throws SecurityException if this would violate user settings
+     */
     public void deleteProvisionedVpnProfile() {
         try {
             mService.deleteVpnProfile(mContext.getOpPackageName());
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index e6ad917..c2f6794 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -365,12 +365,16 @@
     }
 
     /**
+     * This method returns true when the parcel is 'definitely' empty.
+     * That is, it may return false for an empty parcel. But will never return true for a non-empty
+     * one.
+     *
      * @hide this should probably be the implementation of isEmpty().  To do that we
      * need to ensure we always use the special empty parcel form when the bundle is
      * empty.  (This may already be the case, but to be safe we'll do this later when
      * we aren't trying to stabilize.)
      */
-    public boolean maybeIsEmpty() {
+    public boolean isDefinitelyEmpty() {
         if (isParcelled()) {
             return isEmptyParcel();
         } else {
@@ -402,6 +406,9 @@
         if (other == null) {
             return false;
         }
+        if (isDefinitelyEmpty() && other.isDefinitelyEmpty()) {
+            return true;
+        }
         if (isParcelled() != other.isParcelled()) {
             // Big kind-of here!
             return false;
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 5cb3361..50d8d80 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -96,9 +96,6 @@
 
     private static final String TAG = "DynSystemClient";
 
-    private static final long DEFAULT_USERDATA_SIZE = (10L << 30);
-
-
     /** Listener for installation status updates. */
     public interface OnStatusChangedListener {
         /**
@@ -386,7 +383,7 @@
     @SystemApi
     @TestApi
     public void start(@NonNull Uri systemUrl, @BytesLong long systemSize) {
-        start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
+        start(systemUrl, systemSize, 0 /* Use the default userdata size */);
     }
 
     /**
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index cbf531c..17851ad 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -19,6 +19,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.gsi.AvbPublicKey;
 import android.gsi.GsiProgress;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -85,6 +86,23 @@
                 throw new RuntimeException(e.toString());
             }
         }
+
+        /**
+         * Retrieve AVB public key from installing partition.
+         *
+         * @param dst           Output the AVB public key.
+         * @return              true on success, false if partition doesn't have a
+         *                      valid VBMeta block to retrieve the AVB key from.
+         */
+        @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+        public boolean getAvbPublicKey(AvbPublicKey dst) {
+            try {
+                return mService.getAvbPublicKey(dst);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e.toString());
+            }
+        }
+
         /**
          * Finish write and make device to boot into the it after reboot.
          *
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index cc32f99..a1f9272 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -15,6 +15,7 @@
  */
 package android.os.image;
 
+import android.gsi.AvbPublicKey;
 import android.gsi.GsiProgress;
 
 /** {@hide} */
@@ -108,4 +109,13 @@
      * @return              true on success, false otherwise.
      */
     boolean submitFromAshmem(long bytes);
+
+    /**
+     * Retrieve AVB public key from installing partition.
+     *
+     * @param dst           Output the AVB public key.
+     * @return              true on success, false if partition doesn't have a
+     *                      valid VBMeta block to retrieve the AVB key from.
+     */
+    boolean getAvbPublicKey(out AvbPublicKey dst);
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3842def..dcbbb70 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9739,11 +9739,17 @@
         private static final Validator BUGREPORT_IN_POWER_MENU_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
-         * Whether ADB is enabled.
+         * Whether ADB over USB is enabled.
          */
         public static final String ADB_ENABLED = "adb_enabled";
 
         /**
+         * Whether ADB over Wifi is enabled.
+         * @hide
+         */
+        public static final String ADB_WIFI_ENABLED = "adb_wifi_enabled";
+
+        /**
          * Whether Views are allowed to save their attribute data.
          * @hide
          */
diff --git a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
index de90b94..2a809b1 100644
--- a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
+++ b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -47,7 +46,6 @@
  * CarrierMessagingService.
  * @hide
  */
-@SystemApi
 public abstract class CarrierMessagingServiceWrapper {
     // Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete
     // prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized.
@@ -64,7 +62,6 @@
      * @return true upon successfully binding to a carrier messaging service, false otherwise
      * @hide
      */
-    @SystemApi
     public boolean bindToCarrierMessagingService(@NonNull Context context,
             @NonNull String carrierPackageName) {
         Preconditions.checkState(mCarrierMessagingServiceConnection == null);
@@ -82,7 +79,6 @@
      * @param context the context
      * @hide
      */
-    @SystemApi
     public void disposeConnection(@NonNull Context context) {
         Preconditions.checkNotNull(mCarrierMessagingServiceConnection);
         context.unbindService(mCarrierMessagingServiceConnection);
@@ -93,7 +89,6 @@
      * Implemented by subclasses to use the carrier messaging service once it is ready.
      * @hide
      */
-    @SystemApi
     public abstract void onServiceReady();
 
     /**
@@ -117,7 +112,6 @@
      * @param callback the callback to notify upon completion
      * @hide
      */
-    @SystemApi
     public void filterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort,
             int subId, @NonNull final CarrierMessagingCallbackWrapper callback) {
         if (mICarrierMessagingService != null) {
@@ -142,7 +136,6 @@
      * @param callback the callback to notify upon completion
      * @hide
      */
-    @SystemApi
     public void sendTextSms(@NonNull String text, int subId, @NonNull String destAddress,
             int sendSmsFlag, @NonNull final CarrierMessagingCallbackWrapper callback) {
         if (mICarrierMessagingService != null) {
@@ -168,7 +161,6 @@
      * @param callback the callback to notify upon completion
      * @hide
      */
-    @SystemApi
     public void sendDataSms(@NonNull byte[] data, int subId, @NonNull String destAddress,
             int destPort, int sendSmsFlag,
             @NonNull final CarrierMessagingCallbackWrapper callback) {
@@ -194,7 +186,6 @@
      * @param callback the callback to notify upon completion
      * @hide
      */
-    @SystemApi
     public void sendMultipartTextSms(@NonNull List<String> parts, int subId,
             @NonNull String destAddress, int sendSmsFlag,
             @NonNull final CarrierMessagingCallbackWrapper callback) {
@@ -220,7 +211,6 @@
      * @param callback the callback to notify upon completion
      * @hide
      */
-    @SystemApi
     public void sendMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
             @NonNull final CarrierMessagingCallbackWrapper callback) {
         if (mICarrierMessagingService != null) {
@@ -244,7 +234,6 @@
      * @param callback the callback to notify upon completion
      * @hide
      */
-    @SystemApi
     public void downloadMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
             @NonNull final CarrierMessagingCallbackWrapper callback) {
         if (mICarrierMessagingService != null) {
@@ -276,7 +265,6 @@
      * {@link CarrierMessagingServiceWrapper}.
      * @hide
      */
-    @SystemApi
     public abstract static class CarrierMessagingCallbackWrapper {
 
         /**
@@ -289,7 +277,6 @@
          *               {@see CarrierMessagingService#onReceiveTextSms}.
          * @hide
          */
-        @SystemApi
         public void onFilterComplete(int result) {
 
         }
@@ -304,7 +291,6 @@
          *                   only if result is {@link CarrierMessagingService#SEND_STATUS_OK}.
          * @hide
          */
-        @SystemApi
         public void onSendSmsComplete(int result, int messageRef) {
 
         }
@@ -319,7 +305,6 @@
          *                    {@link CarrierMessagingService#SEND_STATUS_OK}.
          * @hide
          */
-        @SystemApi
         public void onSendMultipartSmsComplete(int result, @Nullable int[] messageRefs) {
 
         }
@@ -334,7 +319,6 @@
          *                    {@link CarrierMessagingService#SEND_STATUS_OK}.
          * @hide
          */
-        @SystemApi
         public void onSendMmsComplete(int result, @Nullable byte[] sendConfPdu) {
 
         }
@@ -346,7 +330,6 @@
          *               and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
          * @hide
          */
-        @SystemApi
         public void onDownloadMmsComplete(int result) {
 
         }
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index a242294..ddc38bf 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -18,7 +18,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.content.Context;
 import android.os.Binder;
@@ -61,7 +60,6 @@
  *
  * @hide
  */
-@SystemApi
 public class TelephonyRegistryManager {
 
     private static final String TAG = "TelephonyRegistryManager";
@@ -250,7 +248,6 @@
      * @param incomingNumber incoming phone number.
      * @hide
      */
-    @SystemApi
     @TestApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void notifyCallStateChangedForAllSubscriptions(@CallState int state,
diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS
index 265674a..c6f42f7 100644
--- a/core/java/android/view/accessibility/OWNERS
+++ b/core/java/android/view/accessibility/OWNERS
@@ -1,3 +1,4 @@
 svetoslavganov@google.com
 pweaver@google.com
 rhedjao@google.com
+qasid@google.com
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 15b1d75..9c8ab0c 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -133,7 +133,7 @@
     void noteNetworkStatsEnabled();
     void noteDeviceIdleMode(int mode, String activeReason, int activeUid);
     void setBatteryState(int status, int health, int plugType, int level, int temp, int volt,
-            int chargeUAh, int chargeFullUAh);
+            int chargeUAh, int chargeFullUAh, long chargeTimeToFullSeconds);
     @UnsupportedAppUsage
     long getAwakeTimeBattery();
     long getAwakeTimePlugged();
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index bbae027..23b1ab5 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.net.Ikev2VpnProfile;
 import android.net.ProxyInfo;
 import android.os.Build;
 import android.os.Parcel;
@@ -332,15 +333,38 @@
         return builder.toString().getBytes(StandardCharsets.UTF_8);
     }
 
+    /** Checks if this profile specifies a LegacyVpn type. */
+    public static boolean isLegacyType(int type) {
+        switch (type) {
+            case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: // fall through
+            case VpnProfile.TYPE_IKEV2_IPSEC_RSA: // fall through
+            case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+                return false;
+            default:
+                return true;
+        }
+    }
+
+    private boolean isValidLockdownLegacyVpnProfile() {
+        return isLegacyType(type) && isServerAddressNumeric() && hasDns()
+                && areDnsAddressesNumeric();
+    }
+
+    private boolean isValidLockdownPlatformVpnProfile() {
+        return Ikev2VpnProfile.isValidVpnProfile(this);
+    }
+
     /**
-     * Tests if profile is valid for lockdown, which requires IPv4 address for both server and DNS.
-     * Server hostnames would require using DNS before connection.
+     * Tests if profile is valid for lockdown.
+     *
+     * <p>For LegacyVpn profiles, this requires an IPv4 address for both the server and DNS.
+     *
+     * <p>For PlatformVpn profiles, this requires a server, an identifier and the relevant fields to
+     * be non-null.
      */
     public boolean isValidLockdownProfile() {
         return isTypeValidForLockdown()
-                && isServerAddressNumeric()
-                && hasDns()
-                && areDnsAddressesNumeric();
+                && (isValidLockdownLegacyVpnProfile() || isValidLockdownPlatformVpnProfile());
     }
 
     /** Returns {@code true} if the VPN type is valid for lockdown. */
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 183c0fb..3d6f233 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -996,6 +996,8 @@
     private int mMinLearnedBatteryCapacity = -1;
     private int mMaxLearnedBatteryCapacity = -1;
 
+    private long mBatteryTimeToFullSeconds = -1;
+
     private long[] mCpuFreqs;
 
     @VisibleForTesting
@@ -12218,7 +12220,7 @@
     @GuardedBy("this")
     public void setBatteryStateLocked(final int status, final int health, final int plugType,
             final int level, /* not final */ int temp, final int volt, final int chargeUAh,
-            final int chargeFullUAh) {
+            final int chargeFullUAh, final long chargeTimeToFullSeconds) {
         // Temperature is encoded without the signed bit, so clamp any negative temperatures to 0.
         temp = Math.max(0, temp);
 
@@ -12421,6 +12423,8 @@
             mMinLearnedBatteryCapacity = Math.min(mMinLearnedBatteryCapacity, chargeFullUAh);
         }
         mMaxLearnedBatteryCapacity = Math.max(mMaxLearnedBatteryCapacity, chargeFullUAh);
+
+        mBatteryTimeToFullSeconds = chargeTimeToFullSeconds;
     }
 
     public static boolean isOnBattery(int plugType, int status) {
@@ -12570,19 +12574,10 @@
             // Not yet working.
             return -1;
         }
-        /* Broken
-        int curLevel = mCurrentBatteryLevel;
-        int plugLevel = mDischargePlugLevel;
-        if (plugLevel < 0 || curLevel < (plugLevel+1)) {
-            return -1;
+        if (mBatteryTimeToFullSeconds >= 0) {
+            return mBatteryTimeToFullSeconds * (1000 * 1000); // s to us
         }
-        long duration = computeBatteryRealtime(curTime, STATS_SINCE_UNPLUGGED);
-        if (duration < 1000*1000) {
-            return -1;
-        }
-        long usPerLevel = duration/(curLevel-plugLevel);
-        return usPerLevel * (100-curLevel);
-        */
+        // Else use algorithmic approach
         if (mChargeStepTracker.mNumStepDurations < 1) {
             return -1;
         }
@@ -12590,7 +12585,7 @@
         if (msPerLevel <= 0) {
             return -1;
         }
-        return (msPerLevel * (100-mCurrentBatteryLevel)) * 1000;
+        return (msPerLevel * (100 - mCurrentBatteryLevel)) * 1000;
     }
 
     /*@hide */
diff --git a/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java
new file mode 100644
index 0000000..26f81d9
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Reads cpu time bpf maps.
+ *
+ * It is implemented as singletons for each separate set of per-UID times. Get___Instance() method
+ * returns the corresponding reader instance. In order to prevent frequent GC, it reuses the same
+ * SparseArray to store data read from BPF maps.
+ *
+ * A KernelCpuUidBpfMapReader instance keeps an error counter. When the number of read errors within
+ * that instance accumulates to 5, this instance will reject all further read requests.
+ *
+ * Data fetched within last 500ms is considered fresh, since the reading lifecycle can take up to
+ * 25ms. KernelCpuUidBpfMapReader always tries to use cache if it is fresh and valid, but it can
+ * be disabled through a parameter.
+ *
+ * A KernelCpuUidBpfMapReader instance is thread-safe. It acquires a write lock when reading the bpf
+ * map, releases it right after, then acquires a read lock before returning a BpfMapIterator. Caller
+ * is responsible for closing BpfMapIterator (also auto-closable) after reading, otherwise deadlock
+ * will occur.
+ */
+public abstract class KernelCpuUidBpfMapReader {
+    private static final int ERROR_THRESHOLD = 5;
+    private static final long FRESHNESS_MS = 500L;
+
+    private static final KernelCpuUidBpfMapReader FREQ_TIME_READER =
+        new KernelCpuUidFreqTimeBpfMapReader();
+
+    private static final KernelCpuUidBpfMapReader ACTIVE_TIME_READER =
+        new KernelCpuUidActiveTimeBpfMapReader();
+
+    private static final KernelCpuUidBpfMapReader CLUSTER_TIME_READER =
+        new KernelCpuUidClusterTimeBpfMapReader();
+
+    static KernelCpuUidBpfMapReader getFreqTimeReaderInstance() {
+        return FREQ_TIME_READER;
+    }
+
+    static KernelCpuUidBpfMapReader getActiveTimeReaderInstance() {
+        return ACTIVE_TIME_READER;
+    }
+
+    static KernelCpuUidBpfMapReader getClusterTimeReaderInstance() {
+        return CLUSTER_TIME_READER;
+    }
+
+    final String mTag = this.getClass().getSimpleName();
+    private int mErrors = 0;
+    private boolean mTracking = false;
+    protected SparseArray<long[]> mData = new SparseArray<>();
+    private long mLastReadTime = 0;
+    protected final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
+    protected final ReentrantReadWriteLock.ReadLock mReadLock = mLock.readLock();
+    protected final ReentrantReadWriteLock.WriteLock mWriteLock = mLock.writeLock();
+
+    public native boolean startTrackingBpfTimes();
+
+    protected abstract boolean readBpfData();
+
+    /**
+     * Returns an array of metadata used to inform the caller of 1) the size of array required by
+     * getNextUid and 2) how to interpret the raw data copied to that array.
+     */
+    public abstract long[] getDataDimensions();
+
+    public void removeUidsInRange(int startUid, int endUid) {
+        if (mErrors > ERROR_THRESHOLD) {
+            return;
+        }
+        mWriteLock.lock();
+        int firstIndex = mData.indexOfKey(startUid);
+        int lastIndex = mData.indexOfKey(endUid);
+        mData.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+        mWriteLock.unlock();
+    }
+
+    public BpfMapIterator open() {
+        return open(false);
+    }
+
+    public BpfMapIterator open(boolean ignoreCache) {
+        if (mErrors > ERROR_THRESHOLD) {
+            return null;
+        }
+        if (!mTracking && !startTrackingBpfTimes()) {
+            Slog.w(mTag, "Failed to start tracking");
+            mErrors++;
+            return null;
+        }
+        if (ignoreCache) {
+            mWriteLock.lock();
+        } else {
+            mReadLock.lock();
+            if (dataValid()) {
+                return new BpfMapIterator();
+            }
+            mReadLock.unlock();
+            mWriteLock.lock();
+            if (dataValid()) {
+                mReadLock.lock();
+                mWriteLock.unlock();
+                return new BpfMapIterator();
+            }
+        }
+        if (readBpfData()) {
+            mLastReadTime = SystemClock.elapsedRealtime();
+            mReadLock.lock();
+            mWriteLock.unlock();
+            return new BpfMapIterator();
+        }
+
+        mWriteLock.unlock();
+        mErrors++;
+        Slog.w(mTag, "Failed to read bpf times");
+        return null;
+    }
+
+    private boolean dataValid() {
+        return mData.size() > 0 && (SystemClock.elapsedRealtime() - mLastReadTime < FRESHNESS_MS);
+    }
+
+    public class BpfMapIterator implements AutoCloseable {
+        private int mPos;
+
+        public BpfMapIterator() {
+        };
+
+        public boolean getNextUid(long[] buf) {
+            if (mPos >= mData.size()) {
+                return false;
+            }
+            buf[0] = mData.keyAt(mPos);
+            System.arraycopy(mData.valueAt(mPos), 0, buf, 1, mData.valueAt(mPos).length);
+            mPos++;
+            return true;
+        }
+
+        public void close() {
+            mReadLock.unlock();
+        }
+    }
+
+    public static class KernelCpuUidFreqTimeBpfMapReader extends KernelCpuUidBpfMapReader {
+
+        private final native boolean removeUidRange(int startUid, int endUid);
+
+        @Override
+        protected final native boolean readBpfData();
+
+        @Override
+        public final native long[] getDataDimensions();
+
+        @Override
+        public void removeUidsInRange(int startUid, int endUid) {
+            mWriteLock.lock();
+            super.removeUidsInRange(startUid, endUid);
+            removeUidRange(startUid, endUid);
+            mWriteLock.unlock();
+        }
+    }
+
+    public static class KernelCpuUidActiveTimeBpfMapReader extends KernelCpuUidBpfMapReader {
+
+        @Override
+        protected final native boolean readBpfData();
+
+        @Override
+        public final native long[] getDataDimensions();
+    }
+
+    public static class KernelCpuUidClusterTimeBpfMapReader extends KernelCpuUidBpfMapReader {
+
+        @Override
+        protected final native boolean readBpfData();
+
+        @Override
+        public final native long[] getDataDimensions();
+    }
+}
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
index e6d044f..34e75fe 100644
--- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -28,6 +28,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
+import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;
 
 import java.io.BufferedReader;
 import java.io.FileWriter;
@@ -57,6 +58,8 @@
     final SparseArray<T> mLastTimes = new SparseArray<>();
     final KernelCpuProcStringReader mReader;
     final boolean mThrottle;
+    protected boolean mBpfTimesAvailable;
+    final KernelCpuUidBpfMapReader mBpfReader;
     private long mMinTimeBetweenRead = DEFAULT_MIN_TIME_BETWEEN_READ;
     private long mLastReadTimeMs = 0;
 
@@ -73,9 +76,15 @@
         void onUidCpuTime(int uid, T time);
     }
 
-    KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+    KernelCpuUidTimeReader(KernelCpuProcStringReader reader, @Nullable KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
         mReader = reader;
         mThrottle = throttle;
+        mBpfReader = bpfReader;
+        mBpfTimesAvailable = (mBpfReader != null);
+    }
+
+    KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+        this(reader, null, throttle);
     }
 
     /**
@@ -151,9 +160,13 @@
         }
         mLastTimes.put(startUid, null);
         mLastTimes.put(endUid, null);
-        final int firstIndex = mLastTimes.indexOfKey(startUid);
-        final int lastIndex = mLastTimes.indexOfKey(endUid);
+        int firstIndex = mLastTimes.indexOfKey(startUid);
+        int lastIndex = mLastTimes.indexOfKey(endUid);
         mLastTimes.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+
+        if (mBpfTimesAvailable) {
+            mBpfReader.removeUidsInRange(startUid, endUid);
+        }
     }
 
     /**
@@ -323,13 +336,13 @@
 
         public KernelCpuUidFreqTimeReader(boolean throttle) {
             this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
-                    throttle);
+                 KernelCpuUidBpfMapReader.getFreqTimeReaderInstance(), throttle);
         }
 
         @VisibleForTesting
         public KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
-                boolean throttle) {
-            super(reader, throttle);
+                KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
+            super(reader, bpfReader, throttle);
             mProcFilePath = Paths.get(procFile);
         }
 
@@ -370,19 +383,24 @@
             if (!mAllUidTimesAvailable) {
                 return null;
             }
-            final int oldMask = StrictMode.allowThreadDiskReadsMask();
-            try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
-                if (readFreqs(reader.readLine()) == null) {
+            if (mBpfTimesAvailable) {
+                readFreqsThroughBpf();
+            }
+            if (mCpuFreqs == null) {
+                final int oldMask = StrictMode.allowThreadDiskReadsMask();
+                try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
+                    if (readFreqs(reader.readLine()) == null) {
+                        return null;
+                    }
+                } catch (IOException e) {
+                    if (++mErrors >= MAX_ERROR_COUNT) {
+                        mAllUidTimesAvailable = false;
+                    }
+                    Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
                     return null;
+                } finally {
+                    StrictMode.setThreadPolicyMask(oldMask);
                 }
-            } catch (IOException e) {
-                if (++mErrors >= MAX_ERROR_COUNT) {
-                    mAllUidTimesAvailable = false;
-                }
-                Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
-                return null;
-            } finally {
-                StrictMode.setThreadPolicyMask(oldMask);
             }
             // Check if the freqs in the proc file correspond to per-cluster freqs.
             final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
@@ -402,6 +420,21 @@
             return mCpuFreqs;
         }
 
+        private long[] readFreqsThroughBpf() {
+            if (!mBpfTimesAvailable || mBpfReader == null) {
+                return null;
+            }
+            mCpuFreqs = mBpfReader.getDataDimensions();
+            if (mCpuFreqs == null) {
+                return null;
+            }
+            mFreqCount = mCpuFreqs.length;
+            mCurTimes = new long[mFreqCount];
+            mDeltaTimes = new long[mFreqCount];
+            mBuffer = new long[mFreqCount + 1];
+            return mCpuFreqs;
+        }
+
         private long[] readFreqs(String line) {
             if (line == null) {
                 return null;
@@ -422,8 +455,45 @@
             return mCpuFreqs;
         }
 
+        private void processUidDelta(@Nullable Callback<long[]> cb) {
+            final int uid = (int) mBuffer[0];
+            long[] lastTimes = mLastTimes.get(uid);
+            if (lastTimes == null) {
+                lastTimes = new long[mFreqCount];
+                mLastTimes.put(uid, lastTimes);
+            }
+            copyToCurTimes();
+            boolean notify = false;
+            boolean valid = true;
+            for (int i = 0; i < mFreqCount; i++) {
+                // Unit is 10ms.
+                mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
+                if (mDeltaTimes[i] < 0) {
+                    Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
+                    valid = false;
+                }
+                notify |= mDeltaTimes[i] > 0;
+            }
+            if (notify && valid) {
+                System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
+                if (cb != null) {
+                    cb.onUidCpuTime(uid, mDeltaTimes);
+                }
+            }
+        }
+
         @Override
         void readDeltaImpl(@Nullable Callback<long[]> cb) {
+            if (mBpfTimesAvailable) {
+                try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+                    if (checkPrecondition(iter)) {
+                        while (iter.getNextUid(mBuffer)) {
+                            processUidDelta(cb);
+                        }
+                        return;
+                    }
+                }
+            }
             try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                 if (!checkPrecondition(iter)) {
                     return;
@@ -434,36 +504,24 @@
                         Slog.wtf(mTag, "Invalid line: " + buf.toString());
                         continue;
                     }
-                    final int uid = (int) mBuffer[0];
-                    long[] lastTimes = mLastTimes.get(uid);
-                    if (lastTimes == null) {
-                        lastTimes = new long[mFreqCount];
-                        mLastTimes.put(uid, lastTimes);
-                    }
-                    copyToCurTimes();
-                    boolean notify = false;
-                    boolean valid = true;
-                    for (int i = 0; i < mFreqCount; i++) {
-                        // Unit is 10ms.
-                        mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
-                        if (mDeltaTimes[i] < 0) {
-                            Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
-                            valid = false;
-                        }
-                        notify |= mDeltaTimes[i] > 0;
-                    }
-                    if (notify && valid) {
-                        System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
-                        if (cb != null) {
-                            cb.onUidCpuTime(uid, mDeltaTimes);
-                        }
-                    }
+                    processUidDelta(cb);
                 }
             }
         }
 
         @Override
         void readAbsoluteImpl(Callback<long[]> cb) {
+            if (mBpfTimesAvailable) {
+                try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+                    if (checkPrecondition(iter)) {
+                        while (iter.getNextUid(mBuffer)) {
+                            copyToCurTimes();
+                            cb.onUidCpuTime((int) mBuffer[0], mCurTimes);
+                        }
+                        return;
+                    }
+                }
+            }
             try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                 if (!checkPrecondition(iter)) {
                     return;
@@ -481,11 +539,24 @@
         }
 
         private void copyToCurTimes() {
+            long factor = mBpfTimesAvailable ? 1 : 10;
             for (int i = 0; i < mFreqCount; i++) {
-                mCurTimes[i] = mBuffer[i + 1] * 10;
+                mCurTimes[i] = mBuffer[i + 1] * factor;
             }
         }
 
+        private boolean checkPrecondition(BpfMapIterator iter) {
+            if (iter == null) {
+                mBpfTimesAvailable = false;
+                return false;
+            }
+            if (mCpuFreqs != null) {
+                return true;
+            }
+            mBpfTimesAvailable = (readFreqsThroughBpf() != null);
+            return mBpfTimesAvailable;
+        }
+
         private boolean checkPrecondition(ProcFileIterator iter) {
             if (iter == null || !iter.hasNextLine()) {
                 // Error logged in KernelCpuProcStringReader.
@@ -544,16 +615,43 @@
         private long[] mBuffer;
 
         public KernelCpuUidActiveTimeReader(boolean throttle) {
-            super(KernelCpuProcStringReader.getActiveTimeReaderInstance(), throttle);
+            super(KernelCpuProcStringReader.getActiveTimeReaderInstance(),
+                  KernelCpuUidBpfMapReader.getActiveTimeReaderInstance(), throttle);
         }
 
         @VisibleForTesting
-        public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
-            super(reader, throttle);
+        public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
+            super(reader, bpfReader, throttle);
+        }
+
+        private void processUidDelta(@Nullable Callback<Long> cb) {
+            int uid = (int) mBuffer[0];
+            long cpuActiveTime = sumActiveTime(mBuffer, mBpfTimesAvailable ? 1 : 10);
+            if (cpuActiveTime > 0) {
+                long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
+                if (delta > 0) {
+                    mLastTimes.put(uid, cpuActiveTime);
+                    if (cb != null) {
+                        cb.onUidCpuTime(uid, delta);
+                    }
+                } else if (delta < 0) {
+                    Slog.e(mTag, "Negative delta from active time proc: " + delta);
+                }
+            }
         }
 
         @Override
         void readDeltaImpl(@Nullable Callback<Long> cb) {
+            if (mBpfTimesAvailable) {
+                try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+                    if (checkPrecondition(iter)) {
+                        while (iter.getNextUid(mBuffer)) {
+                            processUidDelta(cb);
+                        }
+                        return;
+                    }
+                }
+            }
             try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                 if (!checkPrecondition(iter)) {
                     return;
@@ -564,25 +662,30 @@
                         Slog.wtf(mTag, "Invalid line: " + buf.toString());
                         continue;
                     }
-                    int uid = (int) mBuffer[0];
-                    long cpuActiveTime = sumActiveTime(mBuffer);
-                    if (cpuActiveTime > 0) {
-                        long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
-                        if (delta > 0) {
-                            mLastTimes.put(uid, cpuActiveTime);
-                            if (cb != null) {
-                                cb.onUidCpuTime(uid, delta);
-                            }
-                        } else if (delta < 0) {
-                            Slog.e(mTag, "Negative delta from active time proc: " + delta);
-                        }
-                    }
+                    processUidDelta(cb);
                 }
             }
         }
 
+        private void processUidAbsolute(@Nullable Callback<Long> cb) {
+            long cpuActiveTime = sumActiveTime(mBuffer, mBpfTimesAvailable ? 1 : 10);
+            if (cpuActiveTime > 0) {
+                cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
+            }
+        }
+
         @Override
         void readAbsoluteImpl(Callback<Long> cb) {
+            if (mBpfTimesAvailable) {
+                try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+                    if (checkPrecondition(iter)) {
+                        while (iter.getNextUid(mBuffer)) {
+                            processUidAbsolute(cb);
+                        }
+                        return;
+                    }
+                }
+            }
             try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                 if (!checkPrecondition(iter)) {
                     return;
@@ -593,23 +696,38 @@
                         Slog.wtf(mTag, "Invalid line: " + buf.toString());
                         continue;
                     }
-                    long cpuActiveTime = sumActiveTime(mBuffer);
-                    if (cpuActiveTime > 0) {
-                        cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
-                    }
+                    processUidAbsolute(cb);
                 }
             }
         }
 
-        private static long sumActiveTime(long[] times) {
+        private static long sumActiveTime(long[] times, double factor) {
             // UID is stored at times[0].
             double sum = 0;
             for (int i = 1; i < times.length; i++) {
-                sum += (double) times[i] * 10 / i; // Unit is 10ms.
+                sum += (double) times[i] * factor / i; // Unit is 10ms.
             }
             return (long) sum;
         }
 
+        private boolean checkPrecondition(BpfMapIterator iter) {
+            if (iter == null) {
+                mBpfTimesAvailable = false;
+                return false;
+            }
+            if (mCores > 0) {
+                return true;
+            }
+            long[] cores = mBpfReader.getDataDimensions();
+            if (cores == null || cores.length < 1) {
+                mBpfTimesAvailable = false;
+                return false;
+            }
+            mCores = (int) cores[0];
+            mBuffer = new long[mCores + 1];
+            return true;
+        }
+
         private boolean checkPrecondition(ProcFileIterator iter) {
             if (iter == null || !iter.hasNextLine()) {
                 // Error logged in KernelCpuProcStringReader.
@@ -664,16 +782,54 @@
         private long[] mDeltaTime;
 
         public KernelCpuUidClusterTimeReader(boolean throttle) {
-            super(KernelCpuProcStringReader.getClusterTimeReaderInstance(), throttle);
+            super(KernelCpuProcStringReader.getClusterTimeReaderInstance(),
+                  KernelCpuUidBpfMapReader.getClusterTimeReaderInstance(), throttle);
         }
 
         @VisibleForTesting
-        public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
-            super(reader, throttle);
+        public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader,
+                                             KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
+            super(reader, bpfReader, throttle);
+        }
+
+        void processUidDelta(@Nullable Callback<long[]> cb) {
+            int uid = (int) mBuffer[0];
+            long[] lastTimes = mLastTimes.get(uid);
+            if (lastTimes == null) {
+                lastTimes = new long[mNumClusters];
+                mLastTimes.put(uid, lastTimes);
+            }
+            sumClusterTime();
+            boolean valid = true;
+            boolean notify = false;
+            for (int i = 0; i < mNumClusters; i++) {
+                mDeltaTime[i] = mCurTime[i] - lastTimes[i];
+                if (mDeltaTime[i] < 0) {
+                    Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
+                    valid = false;
+                }
+                notify |= mDeltaTime[i] > 0;
+            }
+            if (notify && valid) {
+                System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
+                if (cb != null) {
+                    cb.onUidCpuTime(uid, mDeltaTime);
+                }
+            }
         }
 
         @Override
         void readDeltaImpl(@Nullable Callback<long[]> cb) {
+            if (mBpfTimesAvailable) {
+                try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+                    if (checkPrecondition(iter)) {
+                        while (iter.getNextUid(mBuffer)) {
+                            processUidDelta(cb);
+                        }
+                        return;
+                    }
+                }
+            }
             try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                 if (!checkPrecondition(iter)) {
                     return;
@@ -684,35 +840,24 @@
                         Slog.wtf(mTag, "Invalid line: " + buf.toString());
                         continue;
                     }
-                    int uid = (int) mBuffer[0];
-                    long[] lastTimes = mLastTimes.get(uid);
-                    if (lastTimes == null) {
-                        lastTimes = new long[mNumClusters];
-                        mLastTimes.put(uid, lastTimes);
-                    }
-                    sumClusterTime();
-                    boolean valid = true;
-                    boolean notify = false;
-                    for (int i = 0; i < mNumClusters; i++) {
-                        mDeltaTime[i] = mCurTime[i] - lastTimes[i];
-                        if (mDeltaTime[i] < 0) {
-                            Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
-                            valid = false;
-                        }
-                        notify |= mDeltaTime[i] > 0;
-                    }
-                    if (notify && valid) {
-                        System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
-                        if (cb != null) {
-                            cb.onUidCpuTime(uid, mDeltaTime);
-                        }
-                    }
+                    processUidDelta(cb);
                 }
             }
         }
 
         @Override
         void readAbsoluteImpl(Callback<long[]> cb) {
+            if (mBpfTimesAvailable) {
+                try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+                    if (checkPrecondition(iter)) {
+                        while (iter.getNextUid(mBuffer)) {
+                            sumClusterTime();
+                            cb.onUidCpuTime((int) mBuffer[0], mCurTime);
+                        }
+                        return;
+                    }
+                }
+            }
             try (ProcFileIterator iter = mReader.open(!mThrottle)) {
                 if (!checkPrecondition(iter)) {
                     return;
@@ -730,17 +875,45 @@
         }
 
         private void sumClusterTime() {
+            double factor = mBpfTimesAvailable ? 1 : 10;
             // UID is stored at mBuffer[0].
             int core = 1;
             for (int i = 0; i < mNumClusters; i++) {
                 double sum = 0;
                 for (int j = 1; j <= mCoresOnClusters[i]; j++) {
-                    sum += (double) mBuffer[core++] * 10 / j; // Unit is 10ms.
+                    sum += (double) mBuffer[core++] * factor / j; // Unit is 10ms.
                 }
                 mCurTime[i] = (long) sum;
             }
         }
 
+        private boolean checkPrecondition(BpfMapIterator iter) {
+            if (iter == null) {
+                mBpfTimesAvailable = false;
+                return false;
+            }
+            if (mNumClusters > 0) {
+                return true;
+            }
+            long[] coresOnClusters = mBpfReader.getDataDimensions();
+            if (coresOnClusters == null || coresOnClusters.length < 1) {
+                mBpfTimesAvailable = false;
+                return false;
+            }
+            mNumClusters = coresOnClusters.length;
+            mCoresOnClusters = new int[mNumClusters];
+            int cores = 0;
+            for (int i = 0; i < mNumClusters; i++) {
+                mCoresOnClusters[i] = (int) coresOnClusters[i];
+                cores += mCoresOnClusters[i];
+            }
+            mNumCores = cores;
+            mBuffer = new long[cores + 1];
+            mCurTime = new long[mNumClusters];
+            mDeltaTime = new long[mNumClusters];
+            return true;
+        }
+
         private boolean checkPrecondition(ProcFileIterator iter) {
             if (iter == null || !iter.hasNextLine()) {
                 // Error logged in KernelCpuProcStringReader.
diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
index 3c43a11..fc0ce6f 100644
--- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
@@ -53,6 +53,8 @@
     private int mReadErrorCounter;
     @GuardedBy("this")
     private boolean mSingleUidCpuTimesAvailable = true;
+    @GuardedBy("this")
+    private boolean mBpfTimesAvailable = true;
     // We use the freq count obtained from /proc/uid_time_in_state to decide how many longs
     // to read from each /proc/uid/<uid>/time_in_state. On the first read, verify if this is
     // correct and if not, set {@link #mSingleUidCpuTimesAvailable} to false. This flag will
@@ -62,6 +64,8 @@
 
     private final Injector mInjector;
 
+    private static final native boolean canReadBpfTimes();
+
     KernelSingleUidTimeReader(int cpuFreqsCount) {
         this(cpuFreqsCount, new Injector());
     }
@@ -83,6 +87,18 @@
             if (!mSingleUidCpuTimesAvailable) {
                 return null;
             }
+            if (mBpfTimesAvailable) {
+                final long[] cpuTimesMs = mInjector.readBpfData(uid);
+                if (cpuTimesMs.length == 0) {
+                    mBpfTimesAvailable = false;
+                } else if (!mCpuFreqsCountVerified && cpuTimesMs.length != mCpuFreqsCount) {
+                    mSingleUidCpuTimesAvailable = false;
+                    return null;
+                } else {
+                    mCpuFreqsCountVerified = true;
+                    return computeDelta(uid, cpuTimesMs);
+                }
+            }
             // Read total cpu times from the proc file.
             final String procFile = new StringBuilder(PROC_FILE_DIR)
                     .append(uid)
@@ -230,6 +246,8 @@
         public byte[] readData(String procFile) throws IOException {
             return Files.readAllBytes(Paths.get(procFile));
         }
+
+        public native long[] readBpfData(int uid);
     }
 
     @VisibleForTesting
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index d3fe582..9d0cb01 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -393,6 +393,9 @@
     }
 
     public int getNumCoresInCpuCluster(int cluster) {
+        if (cluster < 0 || cluster >= mCpuClusters.length) {
+            return 0; // index out of bound
+        }
         return mCpuClusters[cluster].numCpus;
     }
 
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 16c0b56..13d0c5c 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -19,8 +19,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ApplicationErrorReport;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.type.DefaultMimeMapFactory;
 import android.os.Build;
@@ -36,7 +34,6 @@
 import com.android.internal.logging.AndroidConfig;
 import com.android.server.NetworkManagementSocketTagger;
 
-import dalvik.annotation.compat.VersionCodes;
 import dalvik.system.RuntimeHooks;
 import dalvik.system.ThreadPrioritySetter;
 import dalvik.system.VMRuntime;
@@ -67,18 +64,8 @@
 
     private static volatile boolean mCrashing = false;
 
-    /**
-     * Native heap allocations will now have a non-zero tag in the most significant byte.
-     * See
-     * <a href="https://source.android.com/devices/tech/debug/tagged-pointers">https://source.android.com/devices/tech/debug/tagged-pointers</a>.
-     */
-    @ChangeId
-    @EnabledAfter(targetSdkVersion = VersionCodes.Q)
-    private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
-
     private static final native void nativeFinishInit();
     private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
-    private static native void nativeDisableHeapPointerTagging();
 
     private static int Clog_e(String tag, String msg, Throwable tr) {
         return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
@@ -411,20 +398,6 @@
         if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
     }
 
-    private static void maybeDisableHeapPointerTagging(long[] disabledCompatChanges) {
-        // Heap tagging needs to be disabled before any additional threads are created, but the
-        // AppCompat framework is not initialized enough at this point.
-        // Check if the change is enabled manually.
-        if (disabledCompatChanges != null) {
-            for (int i = 0; i < disabledCompatChanges.length; i++) {
-                if (disabledCompatChanges[i] == NATIVE_HEAP_POINTER_TAGGING) {
-                    nativeDisableHeapPointerTagging();
-                    break;
-                }
-            }
-        }
-    }
-
     protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
             String[] argv, ClassLoader classLoader) {
         // If the application calls System.exit(), terminate the process
@@ -437,8 +410,6 @@
         VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
         VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
 
-        maybeDisableHeapPointerTagging(disabledCompatChanges);
-
         final Arguments args = new Arguments(argv);
 
         // The end of of the RuntimeInit event (see #zygoteInit).
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 72eb32a..5f196a0 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -121,6 +121,25 @@
      */
     public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18;
 
+    public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20);
+    /**
+     * Enable pointer tagging in this process.
+     * Tags are checked during memory deallocation, but not on access.
+     * TBI stands for Top-Byte-Ignore, an ARM CPU feature.
+     * {@link https://developer.arm.com/docs/den0024/latest/the-memory-management-unit/translation-table-configuration/virtual-address-tagging}
+     */
+    public static final int MEMORY_TAG_LEVEL_TBI = 1 << 19;
+
+    /**
+     * Enable asynchronous memory tag checks in this process.
+     */
+    public static final int MEMORY_TAG_LEVEL_ASYNC = 2 << 19;
+
+    /**
+     * Enable synchronous memory tag checks in this process.
+     */
+    public static final int MEMORY_TAG_LEVEL_SYNC = 3 << 19;
+
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
     /** Default external storage should be mounted. */
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index decc92c..300f71a 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -781,6 +781,10 @@
             Zygote.applyDebuggerSystemProperty(parsedArgs);
             Zygote.applyInvokeWithSystemProperty(parsedArgs);
 
+            /* Enable pointer tagging in the system server unconditionally. Hardware support for
+             * this is present in all ARMv8 CPUs; this flag has no effect on other platforms. */
+            parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+
             if (shouldProfileSystemServer()) {
                 parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
             }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 43df2e4..872f26d1 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -207,6 +207,8 @@
         "com_android_internal_os_AtomicDirectory.cpp",
         "com_android_internal_os_ClassLoaderFactory.cpp",
         "com_android_internal_os_FuseAppLoop.cpp",
+        "com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
+        "com_android_internal_os_KernelSingleUidTimeReader.cpp",
         "com_android_internal_os_Zygote.cpp",
         "com_android_internal_os_ZygoteInit.cpp",
         "com_android_internal_util_VirtualRefBasePtr.cpp",
@@ -303,6 +305,7 @@
         "libdl",
         "libdl_android",
         "libstatslog",
+        "libtimeinstate",
         "server_configurable_flags",
     ],
 
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 5b80af5..a2a6716 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -230,6 +230,8 @@
 extern int register_com_android_internal_os_AtomicDirectory(JNIEnv *env);
 extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
 extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
+extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env);
+extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env);
 extern int register_com_android_internal_os_Zygote(JNIEnv *env);
 extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
 extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
@@ -282,14 +284,6 @@
     gCurRuntime->setExitWithoutCleanup(exitWithoutCleanup);
 }
 
-static void com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging(
-        JNIEnv* env, jobject clazz) {
-    HeapTaggingLevel tag_level = M_HEAP_TAGGING_LEVEL_NONE;
-    if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level))) {
-        ALOGE("ERROR: could not disable heap pointer tagging\n");
-    }
-}
-
 /*
  * JNI registration.
  */
@@ -301,8 +295,6 @@
              (void*)com_android_internal_os_RuntimeInit_nativeFinishInit},
             {"nativeSetExitWithoutCleanup", "(Z)V",
              (void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},
-            {"nativeDisableHeapPointerTagging", "()V",
-             (void*)com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging},
     };
     return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
         methods, NELEM(methods));
@@ -1650,6 +1642,8 @@
     REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
     REG_JNI(register_com_android_internal_os_AtomicDirectory),
     REG_JNI(register_com_android_internal_os_FuseAppLoop),
+    REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader),
+    REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader),
 };
 
 /*
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index 151dbfce..a88f891 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -292,19 +292,11 @@
         return;
     }
 
-    const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL);
+    const char *interfaceName = env->GetStringUTFChars(interfaceNameObj, NULL);
     if (interfaceName) {
-        String8 interfaceNameCopy = String8(String16(
-                reinterpret_cast<const char16_t *>(interfaceName),
-                env->GetStringLength(interfaceNameObj)));
-
-        env->ReleaseStringCritical(interfaceNameObj, interfaceName);
-        interfaceName = NULL;
-
         hardware::Parcel *parcel =
             JHwParcel::GetNativeContext(env, thiz)->getParcel();
-
-        bool valid = parcel->enforceInterface(interfaceNameCopy.string());
+        bool valid = parcel->enforceInterface(interfaceName);
 
         if (!valid) {
             jniThrowException(
@@ -312,6 +304,7 @@
                     "java/lang/SecurityException",
                     "HWBinder invocation to an incorrect interface");
         }
+        env->ReleaseStringUTFChars(interfaceNameObj, interfaceName);
     }
 }
 
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index d80c071..d723ecc 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -625,8 +625,8 @@
             IPCThreadState* threadState = IPCThreadState::self();
             const int32_t oldPolicy = threadState->getStrictModePolicy();
             const bool isValid = parcel->enforceInterface(
-                String16(reinterpret_cast<const char16_t*>(str),
-                         env->GetStringLength(name)),
+                reinterpret_cast<const char16_t*>(str),
+                env->GetStringLength(name),
                 threadState);
             env->ReleaseStringCritical(name, str);
             if (isValid) {
diff --git a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
new file mode 100644
index 0000000..7c68de5
--- /dev/null
+++ b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2020 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 "core_jni_helpers.h"
+
+#include <sys/sysinfo.h>
+
+#include <android-base/stringprintf.h>
+#include <cputimeinstate.h>
+
+namespace android {
+
+static constexpr uint64_t NSEC_PER_MSEC = 1000000;
+
+static struct {
+    jclass clazz;
+    jmethodID put;
+    jmethodID get;
+} gSparseArrayClassInfo;
+
+static jfieldID gmData;
+
+static jlongArray getUidArray(JNIEnv *env, jobject sparseAr, uint32_t uid, jsize sz) {
+    jlongArray ar = (jlongArray)env->CallObjectMethod(sparseAr, gSparseArrayClassInfo.get, uid);
+    if (!ar) {
+        ar = env->NewLongArray(sz);
+        if (ar == NULL) return ar;
+        env->CallVoidMethod(sparseAr, gSparseArrayClassInfo.put, uid, ar);
+    }
+    return ar;
+}
+
+static void copy2DVecToArray(JNIEnv *env, jlongArray ar, std::vector<std::vector<uint64_t>> &vec) {
+    jsize start = 0;
+    for (auto &subVec : vec) {
+        for (uint32_t i = 0; i < subVec.size(); ++i) subVec[i] /= NSEC_PER_MSEC;
+        env->SetLongArrayRegion(ar, start, subVec.size(),
+                                reinterpret_cast<const jlong *>(subVec.data()));
+        start += subVec.size();
+    }
+}
+
+static jboolean KernelCpuUidFreqTimeBpfMapReader_removeUidRange(JNIEnv *env, jclass, jint startUid,
+                                                                jint endUid) {
+    for (uint32_t uid = startUid; uid <= endUid; ++uid) {
+        if (!android::bpf::clearUidTimes(uid)) return false;
+    }
+    return true;
+}
+
+static jboolean KernelCpuUidFreqTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
+    static uint64_t lastUpdate = 0;
+    uint64_t newLastUpdate = lastUpdate;
+    auto sparseAr = env->GetObjectField(thiz, gmData);
+    if (sparseAr == NULL) return false;
+    auto data = android::bpf::getUidsUpdatedCpuFreqTimes(&newLastUpdate);
+    if (!data.has_value()) return false;
+
+    jsize s = 0;
+    for (auto &[uid, times] : *data) {
+        if (s == 0) {
+            for (const auto &subVec : times) s += subVec.size();
+        }
+        jlongArray ar = getUidArray(env, sparseAr, uid, s);
+        if (ar == NULL) return false;
+        copy2DVecToArray(env, ar, times);
+    }
+    lastUpdate = newLastUpdate;
+    return true;
+}
+
+static jlongArray KernelCpuUidFreqTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
+    auto freqs = android::bpf::getCpuFreqs();
+    if (!freqs) return NULL;
+
+    std::vector<uint64_t> allFreqs;
+    for (const auto &vec : *freqs) std::copy(vec.begin(), vec.end(), std::back_inserter(allFreqs));
+
+    auto ar = env->NewLongArray(allFreqs.size());
+    if (ar != NULL) {
+        env->SetLongArrayRegion(ar, 0, allFreqs.size(),
+                                reinterpret_cast<const jlong *>(allFreqs.data()));
+    }
+    return ar;
+}
+
+static const JNINativeMethod gFreqTimeMethods[] = {
+        {"removeUidRange", "(II)Z", (void *)KernelCpuUidFreqTimeBpfMapReader_removeUidRange},
+        {"readBpfData", "()Z", (void *)KernelCpuUidFreqTimeBpfMapReader_readBpfData},
+        {"getDataDimensions", "()[J", (void *)KernelCpuUidFreqTimeBpfMapReader_getDataDimensions},
+};
+
+static jboolean KernelCpuUidActiveTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
+    static uint64_t lastUpdate = 0;
+    uint64_t newLastUpdate = lastUpdate;
+    auto sparseAr = env->GetObjectField(thiz, gmData);
+    if (sparseAr == NULL) return false;
+    auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
+    if (!data.has_value()) return false;
+
+    for (auto &[uid, times] : *data) {
+        // TODO: revise calling code so we can divide by NSEC_PER_MSEC here instead
+        for (auto &time : times.active) time /= NSEC_PER_MSEC;
+        jlongArray ar = getUidArray(env, sparseAr, uid, times.active.size());
+        if (ar == NULL) return false;
+        env->SetLongArrayRegion(ar, 0, times.active.size(),
+                                reinterpret_cast<const jlong *>(times.active.data()));
+    }
+    lastUpdate = newLastUpdate;
+    return true;
+}
+
+static jlongArray KernelCpuUidActiveTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
+    jlong nCpus = get_nprocs_conf();
+
+    auto ar = env->NewLongArray(1);
+    if (ar != NULL) env->SetLongArrayRegion(ar, 0, 1, &nCpus);
+    return ar;
+}
+
+static const JNINativeMethod gActiveTimeMethods[] = {
+        {"readBpfData", "()Z", (void *)KernelCpuUidActiveTimeBpfMapReader_readBpfData},
+        {"getDataDimensions", "()[J", (void *)KernelCpuUidActiveTimeBpfMapReader_getDataDimensions},
+};
+
+static jboolean KernelCpuUidClusterTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
+    static uint64_t lastUpdate = 0;
+    uint64_t newLastUpdate = lastUpdate;
+    auto sparseAr = env->GetObjectField(thiz, gmData);
+    if (sparseAr == NULL) return false;
+    auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
+    if (!data.has_value()) return false;
+
+    jsize s = 0;
+    for (auto &[uid, times] : *data) {
+        if (s == 0) {
+            for (const auto &subVec : times.policy) s += subVec.size();
+        }
+        jlongArray ar = getUidArray(env, sparseAr, uid, s);
+        if (ar == NULL) return false;
+        copy2DVecToArray(env, ar, times.policy);
+    }
+    lastUpdate = newLastUpdate;
+    return true;
+}
+
+static jlongArray KernelCpuUidClusterTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
+    auto times = android::bpf::getUidConcurrentTimes(0);
+    if (!times.has_value()) return NULL;
+
+    std::vector<jlong> clusterCores;
+    for (const auto &vec : times->policy) clusterCores.push_back(vec.size());
+    auto ar = env->NewLongArray(clusterCores.size());
+    if (ar != NULL) env->SetLongArrayRegion(ar, 0, clusterCores.size(), clusterCores.data());
+    return ar;
+}
+
+static const JNINativeMethod gClusterTimeMethods[] = {
+        {"readBpfData", "()Z", (void *)KernelCpuUidClusterTimeBpfMapReader_readBpfData},
+        {"getDataDimensions", "()[J",
+         (void *)KernelCpuUidClusterTimeBpfMapReader_getDataDimensions},
+};
+
+struct readerMethods {
+    const char *name;
+    const JNINativeMethod *methods;
+    int numMethods;
+};
+
+static const readerMethods gAllMethods[] = {
+        {"KernelCpuUidFreqTimeBpfMapReader", gFreqTimeMethods, NELEM(gFreqTimeMethods)},
+        {"KernelCpuUidActiveTimeBpfMapReader", gActiveTimeMethods, NELEM(gActiveTimeMethods)},
+        {"KernelCpuUidClusterTimeBpfMapReader", gClusterTimeMethods, NELEM(gClusterTimeMethods)},
+};
+
+static jboolean KernelCpuUidBpfMapReader_startTrackingBpfTimes(JNIEnv *, jobject) {
+    return android::bpf::startTrackingUidTimes();
+}
+
+int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env) {
+    gSparseArrayClassInfo.clazz = FindClassOrDie(env, "android/util/SparseArray");
+    gSparseArrayClassInfo.clazz = MakeGlobalRefOrDie(env, gSparseArrayClassInfo.clazz);
+    gSparseArrayClassInfo.put =
+            GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "put", "(ILjava/lang/Object;)V");
+    gSparseArrayClassInfo.get =
+            GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "get", "(I)Ljava/lang/Object;");
+    constexpr auto readerName = "com/android/internal/os/KernelCpuUidBpfMapReader";
+    constexpr JNINativeMethod method = {"startTrackingBpfTimes", "()Z",
+                                        (void *)KernelCpuUidBpfMapReader_startTrackingBpfTimes};
+
+    int ret = RegisterMethodsOrDie(env, readerName, &method, 1);
+    if (ret < 0) return ret;
+    auto c = FindClassOrDie(env, readerName);
+    gmData = GetFieldIDOrDie(env, c, "mData", "Landroid/util/SparseArray;");
+
+    for (const auto &m : gAllMethods) {
+        auto fullName = android::base::StringPrintf("%s$%s", readerName, m.name);
+        ret = RegisterMethodsOrDie(env, fullName.c_str(), m.methods, m.numMethods);
+        if (ret < 0) break;
+    }
+    return ret;
+}
+
+} // namespace android
diff --git a/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp b/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp
new file mode 100644
index 0000000..c0ecf33
--- /dev/null
+++ b/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 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 "core_jni_helpers.h"
+
+#include <cputimeinstate.h>
+
+namespace android {
+
+static constexpr uint64_t NSEC_PER_MSEC = 1000000;
+
+static jlongArray copyVecsToArray(JNIEnv *env, std::vector<std::vector<uint64_t>> &vec) {
+    jsize s = 0;
+    for (const auto &subVec : vec) s += subVec.size();
+    jlongArray ar = env->NewLongArray(s);
+    jsize start = 0;
+    for (auto &subVec : vec) {
+        for (uint32_t i = 0; i < subVec.size(); ++i) subVec[i] /= NSEC_PER_MSEC;
+        env->SetLongArrayRegion(ar, start, subVec.size(),
+                                reinterpret_cast<const jlong*>(subVec.data()));
+        start += subVec.size();
+    }
+    return ar;
+}
+
+static jlongArray getUidCpuFreqTimeMs(JNIEnv *env, jclass, jint uid) {
+    auto out = android::bpf::getUidCpuFreqTimes(uid);
+    if (!out) return env->NewLongArray(0);
+    return copyVecsToArray(env, out.value());
+}
+
+static const JNINativeMethod g_single_methods[] = {
+    {"readBpfData", "(I)[J", (void *)getUidCpuFreqTimeMs},
+};
+
+int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env) {
+    return RegisterMethodsOrDie(env, "com/android/internal/os/KernelSingleUidTimeReader$Injector",
+                                g_single_methods, NELEM(g_single_methods));
+}
+
+}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7e4a16d..ea58cbd 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -315,6 +315,8 @@
 enum RuntimeFlags : uint32_t {
   DEBUG_ENABLE_JDWP = 1,
   PROFILE_FROM_SHELL = 1 << 15,
+  MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20),
+  MEMORY_TAG_LEVEL_TBI = 1 << 19,
 };
 
 enum UnsolicitedZygoteMessageTypes : uint32_t {
@@ -1153,6 +1155,16 @@
     }
   }
 
+  HeapTaggingLevel heap_tagging_level;
+  switch (runtime_flags & RuntimeFlags::MEMORY_TAG_LEVEL_MASK) {
+    case RuntimeFlags::MEMORY_TAG_LEVEL_TBI:
+      heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
+      break;
+    default:
+      heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
+  }
+  android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level));
+
   if (NeedsNoRandomizeWorkaround()) {
     // Work around ARM kernel ASLR lossage (http://b/5817320).
     int old_personality = personality(0xffffffff);
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index c217caf..5690af5 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2437,4 +2437,10 @@
     //   > Pair device with QR code > Scan QR code > Pairing device dialog
     // CATEGORY: SETTINGS
     ADB_WIRELESS_DEVICE_QR_PAIRING_DIALOG = 1833;
+
+    // OPEN: Settings > Developer Options > Wireless debugging
+    //   > Click on paired device
+    // CATEGORY: SETTINGS
+    // OS: R
+    ADB_WIRELESS_DEVICE_DETAILS = 1836;
 }
diff --git a/core/proto/android/stats/dnsresolver/dns_resolver.proto b/core/proto/android/stats/dnsresolver/dns_resolver.proto
index 9eaabfb..15ef122 100644
--- a/core/proto/android/stats/dnsresolver/dns_resolver.proto
+++ b/core/proto/android/stats/dnsresolver/dns_resolver.proto
@@ -169,12 +169,20 @@
     NT_BLUETOOTH = 3;
     // Indicates this network uses an Ethernet transport.
     NT_ETHERNET = 4;
-    // Indicates this network uses a VPN transport.
-    NT_VPN = 5;
+    // Indicates this network uses a VPN transport, now deprecated.
+    NT_VPN = 5 [deprecated=true];
     // Indicates this network uses a Wi-Fi Aware transport.
     NT_WIFI_AWARE = 6;
     // Indicates this network uses a LoWPAN transport.
     NT_LOWPAN = 7;
+    // Indicates this network uses a Cellular+VPN transport.
+    NT_CELLULAR_VPN = 8;
+    // Indicates this network uses a Wi-Fi+VPN transport.
+    NT_WIFI_VPN = 9;
+    // Indicates this network uses a Bluetooth+VPN transport.
+    NT_BLUETOOTH_VPN = 10;
+    // Indicates this network uses an Ethernet+VPN transport.
+    NT_ETHERNET_VPN = 11;
 }
 
 enum CacheStatus{
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 64dcfa7..e8cfa27 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -391,6 +391,9 @@
     <protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" />
     <protected-broadcast android:name="android.intent.action.ADVANCED_SETTINGS" />
     <protected-broadcast android:name="android.intent.action.APPLICATION_RESTRICTIONS_CHANGED" />
+    <protected-broadcast android:name="com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES" />
+    <protected-broadcast android:name="com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT" />
+    <protected-broadcast android:name="com.android.server.adb.WIRELESS_DEBUG_STATUS" />
 
     <!-- Legacy -->
     <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
@@ -1640,6 +1643,7 @@
 
     <!-- Allows Settings and SystemUI to call methods in Networking services
          <p>Not for use by third-party or privileged applications.
+         @SystemApi @TestApi
          @hide This should only be used by Settings and SystemUI.
     -->
     <permission android:name="android.permission.NETWORK_SETTINGS"
@@ -1782,6 +1786,9 @@
         android:protectionLevel="normal" />
 
     <!-- @SystemApi Allows an internal user to use privileged SecureElement APIs.
+         Applications holding this permission can access OMAPI reset system API
+         and bypass OMAPI AccessControlEnforcer.
+         <p>Not for use by third-party applications.
          @hide -->
     <permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED"
         android:protectionLevel="signature|privileged" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0541955..1b45d40 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2718,6 +2718,18 @@
     <string name="config_customAdbPublicKeyConfirmationSecondaryUserComponent"
             >com.android.systemui/com.android.systemui.usb.UsbDebuggingSecondaryUserActivity</string>
 
+    <!-- Name of the activity or service that prompts the user to reject, accept, or whitelist
+         a wireless network for wireless debugging.
+         Can be customized for other product types -->
+    <string name="config_customAdbWifiNetworkConfirmationComponent"
+            >com.android.systemui/com.android.systemui.wifi.WifiDebuggingActivity</string>
+
+    <!-- Name of the activity that prompts the secondary user to acknowledge she/he needs to
+         switch to the primary user to enable wireless debugging.
+         Can be customized for other product types -->
+    <string name="config_customAdbWifiNetworkConfirmationSecondaryUserComponent"
+            >com.android.systemui/com.android.systemui.wifi.WifiDebuggingSecondaryUserActivity</string>
+
     <!-- Name of the dialog that is used to request the user's consent for a Platform VPN -->
     <string name="config_platformVpnConfirmDialogComponent" translatable="false"
             >com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog</string>
@@ -3031,6 +3043,10 @@
     <!-- Whether to use voip audio mode for ims call -->
     <bool name="config_use_voip_mode_for_ims">false</bool>
 
+    <!-- Boolean indicating USSD over IMS is allowed.
+     If it is not supported due to modem limitations, USSD send over the CS pipe instead.-->
+    <bool name="config_allow_ussd_over_ims">false</bool>
+
     <!-- String array containing numbers that shouldn't be logged. Country-specific. -->
     <string-array name="unloggable_phone_numbers" />
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9bc0d96..1758702 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3642,6 +3642,12 @@
     <!-- Message of notification shown when ADB is actively connected to the phone. -->
     <string name="adb_active_notification_message">Tap to turn off USB debugging</string>
     <string name="adb_active_notification_message" product="tv">Select to disable USB debugging.</string>
+    <!-- Title of notification shown when ADB Wireless is actively connected to the phone. [CHAR LIMIT=NONE] -->
+    <string name="adbwifi_active_notification_title">Wireless debugging connected</string>
+    <!-- Message of notification shown when ADB Wireless is actively connected to the phone. [CHAR LIMIT=NONE] -->
+    <string name="adbwifi_active_notification_message">Tap to turn off wireless debugging</string>
+    <!-- Message of notification shown when ADB Wireless is actively connected to the TV. [CHAR LIMIT=NONE] -->
+    <string name="adbwifi_active_notification_message" product="tv">Select to disable wireless debugging.</string>
 
     <!-- Title of notification shown when Test Harness Mode is enabled. [CHAR LIMIT=NONE] -->
     <string name="test_harness_mode_notification_title">Test Harness Mode enabled</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d1e449e..188d924 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2090,6 +2090,8 @@
   <java-symbol type="string" name="accessibility_binding_label" />
   <java-symbol type="string" name="adb_active_notification_message" />
   <java-symbol type="string" name="adb_active_notification_title" />
+  <java-symbol type="string" name="adbwifi_active_notification_message" />
+  <java-symbol type="string" name="adbwifi_active_notification_title" />
   <java-symbol type="string" name="test_harness_mode_notification_title" />
   <java-symbol type="string" name="test_harness_mode_notification_message" />
   <java-symbol type="string" name="taking_remote_bugreport_notification_title" />
@@ -2232,6 +2234,8 @@
   <java-symbol type="fraction" name="config_maximumScreenDimRatio" />
   <java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" />
   <java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" />
+  <java-symbol type="string" name="config_customAdbWifiNetworkConfirmationComponent" />
+  <java-symbol type="string" name="config_customAdbWifiNetworkConfirmationSecondaryUserComponent" />
   <java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
   <java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" />
   <java-symbol type="string" name="config_platformVpnConfirmDialogComponent" />
@@ -2623,6 +2627,7 @@
   <java-symbol type="bool" name="config_device_wfc_ims_available" />
   <java-symbol type="bool" name="config_carrier_wfc_ims_available" />
   <java-symbol type="bool" name="config_use_voip_mode_for_ims" />
+  <java-symbol type="bool" name="config_allow_ussd_over_ims" />
   <java-symbol type="attr" name="touchscreenBlocksFocus" />
   <java-symbol type="layout" name="resolver_list_with_default" />
   <java-symbol type="string" name="activity_resolver_set_always" />
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index 899d630..d8ec72f 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -51,6 +51,12 @@
       <value>0.1</value> <!-- ~1mA -->
   </array>
 
+  <!-- Additional power consumption by CPU excluding cluster and core when
+       running -->
+  <array name="cpu.active">
+      <value>0.1</value>
+  </array>
+
   <!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the
        number of CPU cores for that cluster.
 
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index e4dc993..4cc70ba 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -30,12 +30,23 @@
  * Unit tests for bundle that requires accessing hidden APS.  Tests that can be written only with
  * public APIs should go in the CTS counterpart.
  *
- * Run with:
- * bit FrameworksCoreTests:android.os.BundleTest
+ * Run with: atest FrameworksCoreTests:android.os.BundleTest
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class BundleTest {
+
+    /**
+     * Take a bundle, write it to a parcel and return the parcel.
+     */
+    private Parcel getParcelledBundle(Bundle bundle) {
+        final Parcel p = Parcel.obtain();
+        // Don't use p.writeParcelabe(), which would write the creator, which we don't need.
+        bundle.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        return p;
+    }
+
     /**
      * Create a test bundle, parcel it and return the parcel.
      */
@@ -48,12 +59,7 @@
             pipe[1].close();
             source.putParcelable("fd", pipe[0]);
         }
-        final Parcel p = Parcel.obtain();
-        // Don't use p.writeParcelabe(), which would write the creator, which we don't need.
-        source.writeToParcel(p, 0);
-        p.setDataPosition(0);
-
-        return p;
+        return getParcelledBundle(source);
     }
 
     /**
@@ -137,4 +143,78 @@
         checkBundle(b, withFd);
         p.recycle();
     }
+
+    @Test
+    public void kindofEquals_bothUnparcelled_same() {
+        Bundle bundle1 = new Bundle();
+        bundle1.putString("StringKey", "S");
+        bundle1.putInt("IntKey", 2);
+
+        Bundle bundle2 = new Bundle();
+        bundle2.putString("StringKey", "S");
+        bundle2.putInt("IntKey", 2);
+
+        assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
+    }
+
+    @Test
+    public void kindofEquals_bothUnparcelled_different() {
+        Bundle bundle1 = new Bundle();
+        bundle1.putString("StringKey", "S");
+        bundle1.putInt("IntKey", 2);
+
+        Bundle bundle2 = new Bundle();
+        bundle2.putString("StringKey", "T");
+        bundle2.putLong("LongKey", 30L);
+
+        assertFalse(BaseBundle.kindofEquals(bundle1, bundle2));
+    }
+
+    @Test
+    public void kindofEquals_bothParcelled_same() {
+        Bundle bundle1 = new Bundle();
+        bundle1.putString("StringKey", "S");
+        bundle1.putInt("IntKey", 2);
+        bundle1.readFromParcel(getParcelledBundle(bundle1));
+
+        Bundle bundle2 = new Bundle();
+        bundle2.putString("StringKey", "S");
+        bundle2.putInt("IntKey", 2);
+        bundle2.readFromParcel(getParcelledBundle(bundle2));
+
+        assertTrue(bundle1.isParcelled());
+        assertTrue(bundle2.isParcelled());
+        assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
+    }
+
+    @Test
+    public void kindofEquals_bothParcelled_different() {
+        Bundle bundle1 = new Bundle();
+        bundle1.putString("StringKey", "S");
+        bundle1.putInt("IntKey", 2);
+        bundle1.readFromParcel(getParcelledBundle(bundle1));
+
+        Bundle bundle2 = new Bundle();
+        bundle2.putString("StringKey", "T");
+        bundle2.putLong("LongKey", 5);
+        bundle2.readFromParcel(getParcelledBundle(bundle2));
+
+        assertTrue(bundle1.isParcelled());
+        assertTrue(bundle2.isParcelled());
+        assertFalse(BaseBundle.kindofEquals(bundle1, bundle2));
+    }
+
+    @Test
+    public void kindofEquals_ParcelledUnparcelled_empty() {
+        Bundle bundle1 = new Bundle();
+        bundle1.readFromParcel(getParcelledBundle(bundle1));
+
+        Bundle bundle2 = new Bundle();
+
+        assertTrue(bundle1.isParcelled());
+        assertFalse(bundle2.isParcelled());
+        // Even though one is parcelled and the other is not, both are empty, so it should
+        // return true
+        assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
+    }
 }
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index e60e555..4b1691d 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -104,6 +104,7 @@
                     Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED,
                     Settings.Global.ADB_ALLOWED_CONNECTION_TIME,
                     Settings.Global.ADB_ENABLED,
+                    Settings.Global.ADB_WIFI_ENABLED,
                     Settings.Global.ADD_USERS_WHEN_LOCKED,
                     Settings.Global.AIRPLANE_MODE_ON,
                     Settings.Global.AIRPLANE_MODE_RADIOS,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index a6329298..2ad8e18 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -40,6 +40,7 @@
         BatteryStatsUserLifecycleTests.class,
         KernelCpuProcStringReaderTest.class,
         KernelCpuUidActiveTimeReaderTest.class,
+        KernelCpuUidBpfMapReaderTest.class,
         KernelCpuUidClusterTimeReaderTest.class,
         KernelCpuUidFreqTimeReaderTest.class,
         KernelCpuUidUserSysTimeReaderTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
index 1b13a99..2ccd74e 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
@@ -21,11 +21,11 @@
 
 import android.content.Context;
 import android.os.FileUtils;
+import android.util.SparseArray;
 import android.util.SparseLongArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
 
@@ -33,11 +33,15 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Random;
 
 /**
@@ -46,14 +50,16 @@
  * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidActiveTimeReaderTest
  */
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 public class KernelCpuUidActiveTimeReaderTest {
     private File mTestDir;
     private File mTestFile;
     private KernelCpuUidActiveTimeReader mReader;
+    private KernelCpuUidTestBpfMapReader mBpfMapReader;
     private VerifiableCallback mCallback;
 
     private Random mRand = new Random(12345);
+    protected boolean mUseBpf;
     private final int mCpus = 4;
     private final String mHeadline = "cpus: 4\n";
     private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
@@ -62,12 +68,22 @@
         return InstrumentationRegistry.getContext();
     }
 
+    @Parameters(name="useBpf={0}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] { {true}, {false} });
+    }
+
+    public KernelCpuUidActiveTimeReaderTest(boolean useBpf) {
+        mUseBpf = useBpf;
+    }
+
     @Before
     public void setUp() {
         mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
         mTestFile = new File(mTestDir, "test.file");
+        mBpfMapReader = new KernelCpuUidTestBpfMapReader();
         mReader = new KernelCpuUidActiveTimeReader(
-                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
         mCallback = new VerifiableCallback();
     }
 
@@ -80,7 +96,7 @@
     @Test
     public void testReadDelta() throws Exception {
         final long[][] times = increaseTime(new long[mUids.length][mCpus]);
-        writeToFile(mHeadline + uidLines(mUids, times));
+        setCpusAndData(times);
         mReader.readDelta(mCallback);
         for (int i = 0; i < mUids.length; ++i) {
             mCallback.verify(mUids[i], getActiveTime(times[i]));
@@ -90,7 +106,7 @@
         // Verify that a second call will only return deltas.
         mCallback.clear();
         final long[][] newTimes1 = increaseTime(times);
-        writeToFile(mHeadline + uidLines(mUids, newTimes1));
+        setCpusAndData(newTimes1);
         mReader.readDelta(mCallback);
         for (int i = 0; i < mUids.length; ++i) {
             mCallback.verify(mUids[i], getActiveTime(newTimes1[i]) - getActiveTime(times[i]));
@@ -105,7 +121,7 @@
         // Verify that calling with a null callback doesn't result in any crashes
         mCallback.clear();
         final long[][] newTimes2 = increaseTime(newTimes1);
-        writeToFile(mHeadline + uidLines(mUids, newTimes2));
+        setCpusAndData(newTimes2);
         mReader.readDelta(null);
         mCallback.verifyNoMoreInteractions();
 
@@ -113,19 +129,20 @@
         // the previous call had null callback.
         mCallback.clear();
         final long[][] newTimes3 = increaseTime(newTimes2);
+        setCpusAndData(newTimes3);
         writeToFile(mHeadline + uidLines(mUids, newTimes3));
         mReader.readDelta(mCallback);
         for (int i = 0; i < mUids.length; ++i) {
             mCallback.verify(mUids[i], getActiveTime(newTimes3[i]) - getActiveTime(newTimes2[i]));
         }
         mCallback.verifyNoMoreInteractions();
-        assertTrue(mTestFile.delete());
+        clearCpusAndData();
     }
 
     @Test
     public void testReadAbsolute() throws Exception {
         final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
-        writeToFile(mHeadline + uidLines(mUids, times1));
+        setCpusAndData(times1);
         mReader.readAbsolute(mCallback);
         for (int i = 0; i < mUids.length; i++) {
             mCallback.verify(mUids[i], getActiveTime(times1[i]));
@@ -135,19 +152,19 @@
         // Verify that a second call should still return absolute values
         mCallback.clear();
         final long[][] times2 = increaseTime(times1);
-        writeToFile(mHeadline + uidLines(mUids, times2));
+        setCpusAndData(times2);
         mReader.readAbsolute(mCallback);
         for (int i = 0; i < mUids.length; i++) {
             mCallback.verify(mUids[i], getActiveTime(times2[i]));
         }
         mCallback.verifyNoMoreInteractions();
-        assertTrue(mTestFile.delete());
+        clearCpusAndData();
     }
 
     @Test
     public void testReadDeltaDecreasedTime() throws Exception {
         final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
-        writeToFile(mHeadline + uidLines(mUids, times1));
+        setCpusAndData(times1);
         mReader.readDelta(mCallback);
 
         // Verify that there should not be a callback for a particular UID if its time decreases.
@@ -155,19 +172,19 @@
         final long[][] times2 = increaseTime(times1);
         System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
         times2[0][0] = 100;
-        writeToFile(mHeadline + uidLines(mUids, times2));
+        setCpusAndData(times2);
         mReader.readDelta(mCallback);
         for (int i = 1; i < mUids.length; i++) {
             mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
         }
         mCallback.verifyNoMoreInteractions();
-        assertTrue(mTestFile.delete());
+        clearCpusAndData();
 
         // Verify that the internal state was not modified.
         mCallback.clear();
         final long[][] times3 = increaseTime(times2);
         times3[0] = increaseTime(times1)[0];
-        writeToFile(mHeadline + uidLines(mUids, times3));
+        setCpusAndData(times3);
         mReader.readDelta(mCallback);
         mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
         for (int i = 1; i < mUids.length; i++) {
@@ -179,26 +196,26 @@
     @Test
     public void testReadDeltaNegativeTime() throws Exception {
         final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
-        writeToFile(mHeadline + uidLines(mUids, times1));
+        setCpusAndData(times1);
         mReader.readDelta(mCallback);
 
         // Verify that there should not be a callback for a particular UID if its time is -ve.
         mCallback.clear();
         final long[][] times2 = increaseTime(times1);
         times2[0][0] *= -1;
-        writeToFile(mHeadline + uidLines(mUids, times2));
+        setCpusAndData(times2);
         mReader.readDelta(mCallback);
         for (int i = 1; i < mUids.length; i++) {
             mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
         }
         mCallback.verifyNoMoreInteractions();
-        assertTrue(mTestFile.delete());
+        clearCpusAndData();
 
         // Verify that the internal state was not modified.
         mCallback.clear();
         final long[][] times3 = increaseTime(times2);
         times3[0] = increaseTime(times1)[0];
-        writeToFile(mHeadline + uidLines(mUids, times3));
+        setCpusAndData(times3);
         mReader.readDelta(mCallback);
         mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
         for (int i = 1; i < mUids.length; i++) {
@@ -207,6 +224,28 @@
         mCallback.verifyNoMoreInteractions();
     }
 
+    private void setCpusAndData(long[][] times) throws IOException {
+        if (mUseBpf) {
+            mBpfMapReader.setCpus(new long[]{ mCpus });
+            SparseArray<long[]> data = new SparseArray<>();
+            for (int i = 0; i < mUids.length; i++) {
+                data.put(mUids[i], times[i]);
+            }
+            mBpfMapReader.setData(data);
+        } else {
+            writeToFile(mHeadline + uidLines(mUids, times));
+        }
+    }
+
+    private void clearCpusAndData() {
+        if (mUseBpf) {
+            mBpfMapReader.setCpus(null);
+            mBpfMapReader.setData(new SparseArray<>());
+        } else {
+            assertTrue(mTestFile.delete());
+        }
+    }
+
     private String uidLines(int[] uids, long[][] times) {
         StringBuffer sb = new StringBuffer();
         for (int i = 0; i < uids.length; i++) {
@@ -261,4 +300,36 @@
             assertEquals(0, mData.size());
         }
     }
+
+    private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
+        private long[] mCpus;
+        private SparseArray<long[]> mNewData = new SparseArray<>();
+
+        public void setData(SparseArray<long[]> data) {
+            mNewData = data;
+        }
+
+        public void setCpus(long[] cpus) {
+            mCpus = cpus;
+        }
+
+        @Override
+        public final boolean startTrackingBpfTimes() {
+            return true;
+        }
+
+        @Override
+        protected final boolean readBpfData() {
+            if (!mUseBpf) {
+                return false;
+            }
+            mData = mNewData;
+            return true;
+        }
+
+        @Override
+        public final long[] getDataDimensions() {
+            return mCpus;
+        }
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
new file mode 100644
index 0000000..257b388
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2020 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.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.util.SparseArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+
+import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidBpfMapReaderTest {
+    private Random mRand = new Random(12345);
+    private KernelCpuUidTestBpfMapReader mReader;
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() {
+        mReader =  new KernelCpuUidTestBpfMapReader();
+    }
+
+    /**
+     * Tests that reading returns null if readBpfData() fails.
+     */
+    @Test
+    public void testUnsuccessfulRead() {
+        assertEquals(null, mReader.open());
+    }
+
+    /**
+     * Tests that reading will always return null after 5 failures.
+     */
+    @Test
+    public void testReadErrorsLimit() {
+        for (int i = 0; i < 3; i++) {
+            try (BpfMapIterator iter = mReader.open()) {
+                assertNull(iter);
+            }
+        }
+
+        SparseArray<long[]> data = new SparseArray<>();
+        long[] times = {2};
+        data.put(1, new long[]{2});
+        mReader.setData(data);
+        testOpenAndReadData(data);
+
+        mReader.setData(null);
+        for (int i = 0; i < 3; i++) {
+            try (BpfMapIterator iter = mReader.open(true)) {
+                assertNull(iter);
+            }
+        }
+        mReader.setData(data);
+        try (BpfMapIterator iter = mReader.open(true)) {
+            assertNull(iter);
+        }
+    }
+
+    /** Tests getNextUid functionality. */
+    @Test
+    public void testGetNextUid() {
+        final SparseArray<long[]> data = getTestSparseArray(800, 50);
+        mReader.setData(data);
+        testOpenAndReadData(data);
+    }
+
+    @Test
+    public void testConcurrent() throws Exception {
+        final SparseArray<long[]> data = getTestSparseArray(200, 50);
+        final SparseArray<long[]> data1 = getTestSparseArray(180, 70);
+        final List<Throwable> errs = Collections.synchronizedList(new ArrayList<>());
+        mReader.setData(data);
+        // An additional thread for modifying the data.
+        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(11);
+        final CountDownLatch ready = new CountDownLatch(10);
+        final CountDownLatch start = new CountDownLatch(1);
+        final CountDownLatch modify = new CountDownLatch(1);
+        final CountDownLatch done = new CountDownLatch(10);
+
+        for (int i = 0; i < 5; i++) {
+            threadPool.submit(() -> {
+                    ready.countDown();
+                    try {
+                        start.await();
+                        testOpenAndReadData(data);
+                    } catch (Throwable e) {
+                        errs.add(e);
+                    } finally {
+                        done.countDown();
+                    }
+            });
+            threadPool.submit(() -> {
+                    ready.countDown();
+                    try {
+                        start.await();
+                        // Wait for data modification.
+                        modify.await();
+                        testOpenAndReadData(data1);
+                    } catch (Throwable e) {
+                        errs.add(e);
+                    } finally {
+                        done.countDown();
+                    }
+             });
+        }
+
+        assertTrue("Prep timed out", ready.await(100, TimeUnit.MILLISECONDS));
+        start.countDown();
+
+        threadPool.schedule(() -> {
+                mReader.setData(data1);
+                modify.countDown();
+        }, 600, TimeUnit.MILLISECONDS);
+
+        assertTrue("Execution timed out", done.await(3, TimeUnit.SECONDS));
+        threadPool.shutdownNow();
+
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        errs.forEach(e -> e.printStackTrace(pw));
+
+        assertTrue("All Exceptions:\n" + sw.toString(), errs.isEmpty());
+    }
+
+    @Test
+    public void testRemoveUidsInRange() {
+        final SparseArray<long[]> data = getTestSparseArray(200, 50);
+        mReader.setData(data);
+        testOpenAndReadData(data);
+        SparseArray<long[]> changedData = new SparseArray<>();
+        for (int i = 6; i < 200; i++) {
+            changedData.put(i, data.get(i));
+        }
+        mReader.removeUidsInRange(0, 5);
+        testOpenAndReadData(changedData);
+    }
+
+    private void testOpenAndReadData(SparseArray<long[]> expectedData) {
+        try (BpfMapIterator iter = mReader.open()) {
+            long[] actual;
+            if (expectedData.size() > 0) {
+                actual = new long[expectedData.valueAt(0).length + 1];
+            } else {
+                actual = new long[0];
+            }
+            for (int i = 0; i < expectedData.size(); i++) {
+                assertTrue(iter.getNextUid(actual));
+                assertEquals(expectedData.keyAt(i), actual[0]);
+                assertArrayEquals(expectedData.valueAt(i), Arrays.copyOfRange(actual, 1, actual.length));
+            }
+            assertFalse(iter.getNextUid(actual));
+            assertFalse(iter.getNextUid(actual));
+        }
+    }
+
+
+    private SparseArray<long[]> getTestSparseArray(int uids, int numPerUid) {
+        SparseArray<long[]> data = new SparseArray<>();
+        for (int i = 0; i < uids; i++) {
+            data.put(i, mRand.longs(numPerUid, 0, Long.MAX_VALUE).toArray());
+        }
+        return data;
+    }
+
+
+    private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
+        private SparseArray<long[]> mNewData;
+
+        public final void setData(SparseArray<long[]> newData) {
+            mNewData = newData;
+        }
+
+        @Override
+        public final boolean startTrackingBpfTimes() {
+            return true;
+        }
+
+        @Override
+        public final boolean readBpfData() {
+            if (mNewData == null) {
+                return false;
+            }
+            mData = mNewData;
+            return true;
+        }
+
+        @Override
+        public final long[] getDataDimensions() {
+            return null;
+        }
+
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
index 2ea80da..a0dab28 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
@@ -27,7 +27,6 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
 
@@ -35,11 +34,15 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Random;
 
 /**
@@ -48,28 +51,42 @@
  * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidClusterTimeReaderTest
  */
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 public class KernelCpuUidClusterTimeReaderTest {
     private File mTestDir;
     private File mTestFile;
     private KernelCpuUidClusterTimeReader mReader;
+    private KernelCpuUidTestBpfMapReader mBpfMapReader;
     private VerifiableCallback mCallback;
 
     private Random mRand = new Random(12345);
+    protected boolean mUseBpf;
     private final int mCpus = 6;
     private final String mHeadline = "policy0: 4 policy4: 2\n";
+    private final long[] mCores = {4, 2};
     private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
 
     private Context getContext() {
         return InstrumentationRegistry.getContext();
     }
 
+    @Parameters(name="useBpf={0}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] { {true}, {false} });
+    }
+
+    public KernelCpuUidClusterTimeReaderTest(boolean useBpf) {
+        mUseBpf = useBpf;
+    }
+
     @Before
     public void setUp() {
         mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
         mTestFile = new File(mTestDir, "test.file");
+        mBpfMapReader = new KernelCpuUidTestBpfMapReader();
         mReader = new KernelCpuUidClusterTimeReader(
-                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader,
+                false);
         mCallback = new VerifiableCallback();
     }
 
@@ -82,7 +99,7 @@
     @Test
     public void testReadDelta() throws Exception {
         final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
-        writeToFile(mHeadline + uidLines(mUids, times1));
+        setCoresAndData(times1);
         mReader.readDelta(mCallback);
         for (int i = 0; i < mUids.length; ++i) {
             mCallback.verify(mUids[i], clusterTime(times1[i]));
@@ -92,7 +109,7 @@
         // Verify that a second call will only return deltas.
         mCallback.clear();
         final long[][] times2 = increaseTime(times1);
-        writeToFile(mHeadline + uidLines(mUids, times2));
+        setCoresAndData(times2);
         mReader.readDelta(mCallback);
         for (int i = 0; i < mUids.length; ++i) {
             mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
@@ -107,7 +124,7 @@
         // Verify that calling with a null callback doesn't result in any crashes
         mCallback.clear();
         final long[][] times3 = increaseTime(times2);
-        writeToFile(mHeadline + uidLines(mUids, times3));
+        setCoresAndData(times3);
         mReader.readDelta(null);
         mCallback.verifyNoMoreInteractions();
 
@@ -115,19 +132,19 @@
         // the previous call had null callback.
         mCallback.clear();
         final long[][] times4 = increaseTime(times3);
-        writeToFile(mHeadline + uidLines(mUids, times4));
+        setCoresAndData(times4);
         mReader.readDelta(mCallback);
         for (int i = 0; i < mUids.length; ++i) {
             mCallback.verify(mUids[i], subtract(clusterTime(times4[i]), clusterTime(times3[i])));
         }
         mCallback.verifyNoMoreInteractions();
-        assertTrue(mTestFile.delete());
+        clearCoresAndData();
     }
 
     @Test
     public void testReadAbsolute() throws Exception {
         final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
-        writeToFile(mHeadline + uidLines(mUids, times1));
+        setCoresAndData(times1);
         mReader.readAbsolute(mCallback);
         for (int i = 0; i < mUids.length; i++) {
             mCallback.verify(mUids[i], clusterTime(times1[i]));
@@ -137,19 +154,19 @@
         // Verify that a second call should still return absolute values
         mCallback.clear();
         final long[][] times2 = increaseTime(times1);
-        writeToFile(mHeadline + uidLines(mUids, times2));
+        setCoresAndData(times2);
         mReader.readAbsolute(mCallback);
         for (int i = 0; i < mUids.length; i++) {
             mCallback.verify(mUids[i], clusterTime(times2[i]));
         }
         mCallback.verifyNoMoreInteractions();
-        assertTrue(mTestFile.delete());
+        clearCoresAndData();
     }
 
     @Test
     public void testReadDeltaDecreasedTime() throws Exception {
         final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
-        writeToFile(mHeadline + uidLines(mUids, times1));
+        setCoresAndData(times1);
         mReader.readDelta(mCallback);
 
         // Verify that there should not be a callback for a particular UID if its time decreases.
@@ -157,19 +174,19 @@
         final long[][] times2 = increaseTime(times1);
         System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
         times2[0][0] = 100;
-        writeToFile(mHeadline + uidLines(mUids, times2));
+        setCoresAndData(times2);
         mReader.readDelta(mCallback);
         for (int i = 1; i < mUids.length; i++) {
             mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
         }
         mCallback.verifyNoMoreInteractions();
-        assertTrue(mTestFile.delete());
+        clearCoresAndData();
 
         // Verify that the internal state was not modified.
         mCallback.clear();
         final long[][] times3 = increaseTime(times2);
         times3[0] = increaseTime(times1)[0];
-        writeToFile(mHeadline + uidLines(mUids, times3));
+        setCoresAndData(times3);
         mReader.readDelta(mCallback);
         mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
         for (int i = 1; i < mUids.length; i++) {
@@ -181,26 +198,26 @@
     @Test
     public void testReadDeltaNegativeTime() throws Exception {
         final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
-        writeToFile(mHeadline + uidLines(mUids, times1));
+        setCoresAndData(times1);
         mReader.readDelta(mCallback);
 
         // Verify that there should not be a callback for a particular UID if its time decreases.
         mCallback.clear();
         final long[][] times2 = increaseTime(times1);
         times2[0][0] *= -1;
-        writeToFile(mHeadline + uidLines(mUids, times2));
+        setCoresAndData(times2);
         mReader.readDelta(mCallback);
         for (int i = 1; i < mUids.length; i++) {
             mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
         }
         mCallback.verifyNoMoreInteractions();
-        assertTrue(mTestFile.delete());
+        clearCoresAndData();
 
         // Verify that the internal state was not modified.
         mCallback.clear();
         final long[][] times3 = increaseTime(times2);
         times3[0] = increaseTime(times1)[0];
-        writeToFile(mHeadline + uidLines(mUids, times3));
+        setCoresAndData(times3);
         mReader.readDelta(mCallback);
         mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
         for (int i = 1; i < mUids.length; i++) {
@@ -209,6 +226,28 @@
         mCallback.verifyNoMoreInteractions();
     }
 
+    private void setCoresAndData(long[][] times) throws IOException {
+        if (mUseBpf) {
+            mBpfMapReader.setClusterCores(mCores);
+            SparseArray<long[]> data = new SparseArray<>();
+            for (int i = 0; i < mUids.length; i++) {
+                data.put(mUids[i], times[i]);
+            }
+            mBpfMapReader.setData(data);
+        } else {
+            writeToFile(mHeadline + uidLines(mUids, times));
+        }
+    }
+
+    private void clearCoresAndData() {
+        if (mUseBpf) {
+            mBpfMapReader.setClusterCores(null);
+            mBpfMapReader.setData(new SparseArray<>());
+        } else {
+            assertTrue(mTestFile.delete());
+        }
+    }
+
     private long[] clusterTime(long[] times) {
         // Assumes 4 + 2 cores
         return new long[]{times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4,
@@ -277,4 +316,36 @@
             assertEquals(0, mData.size());
         }
     }
+
+    private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
+        private long[] mClusterCores;
+        private SparseArray<long[]> mNewData = new SparseArray<>();
+
+        public void setData(SparseArray<long[]> data) {
+            mNewData = data;
+        }
+
+        public void setClusterCores(long[] cores) {
+            mClusterCores = cores;
+        }
+
+        @Override
+        public final boolean startTrackingBpfTimes() {
+            return true;
+        }
+
+        @Override
+        protected final boolean readBpfData() {
+            if (!mUseBpf) {
+                return false;
+            }
+            mData = mNewData;
+            return true;
+        }
+
+        @Override
+        public final long[] getDataDimensions() {
+            return mClusterCores;
+        }
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
index 0b6fed3..c60a6d6 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
@@ -29,7 +29,6 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
 
@@ -37,6 +36,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -45,6 +46,7 @@
 import java.io.IOException;
 import java.nio.file.Files;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Random;
 
 /**
@@ -53,14 +55,16 @@
  * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidFreqTimeReaderTest
  */
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 public class KernelCpuUidFreqTimeReaderTest {
     private File mTestDir;
     private File mTestFile;
     private KernelCpuUidFreqTimeReader mReader;
+    private KernelCpuUidTestBpfMapReader mBpfMapReader;
     private VerifiableCallback mCallback;
     @Mock
     private PowerProfile mPowerProfile;
+    private boolean mUseBpf;
 
     private Random mRand = new Random(12345);
     private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
@@ -69,13 +73,23 @@
         return InstrumentationRegistry.getContext();
     }
 
+    @Parameters(name="useBpf={0}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] { {true}, {false} });
+    }
+
+    public KernelCpuUidFreqTimeReaderTest(boolean useBpf) {
+        mUseBpf = useBpf;
+    }
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
         mTestFile = new File(mTestDir, "test.file");
+        mBpfMapReader = new KernelCpuUidTestBpfMapReader();
         mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
-                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+                new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
         mCallback = new VerifiableCallback();
     }
 
@@ -97,17 +111,17 @@
         final int[][] numFreqs = {{3, 6}, {4, 5}, {3, 5, 4}, {3}};
         for (int i = 0; i < freqs.length; ++i) {
             mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
-                    new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+                    new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
             setCpuClusterFreqs(numClusters[i], numFreqs[i]);
-            writeToFile(freqsLine(freqs[i]));
+            setFreqs(freqs[i]);
             long[] actualFreqs = mReader.readFreqs(mPowerProfile);
             assertArrayEquals(freqs[i], actualFreqs);
             final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
                     Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
             assertFalse(errMsg, mReader.perClusterTimesAvailable());
 
-            // Verify that a second call won't read the proc file again
-            assertTrue(mTestFile.delete());
+            // Verify that a second call won't re-read the freqs
+            clearFreqsAndData();
             actualFreqs = mReader.readFreqs(mPowerProfile);
             assertArrayEquals(freqs[i], actualFreqs);
             assertFalse(errMsg, mReader.perClusterTimesAvailable());
@@ -125,17 +139,17 @@
         final int[][] numFreqs = {{4}, {3, 5}, {3, 5, 4}};
         for (int i = 0; i < freqs.length; ++i) {
             mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
-                    new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+                    new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
             setCpuClusterFreqs(numClusters[i], numFreqs[i]);
-            writeToFile(freqsLine(freqs[i]));
+            setFreqs(freqs[i]);
             long[] actualFreqs = mReader.readFreqs(mPowerProfile);
             assertArrayEquals(freqs[i], actualFreqs);
             final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
                     Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
             assertTrue(errMsg, mReader.perClusterTimesAvailable());
 
-            // Verify that a second call won't read the proc file again
-            assertTrue(mTestFile.delete());
+            // Verify that a second call won't re-read the freqs
+            clearFreqsAndData();
             actualFreqs = mReader.readFreqs(mPowerProfile);
             assertArrayEquals(freqs[i], actualFreqs);
             assertTrue(errMsg, mReader.perClusterTimesAvailable());
@@ -147,7 +161,7 @@
         final long[] freqs = {110, 123, 145, 167, 289, 997};
         final long[][] times = increaseTime(new long[mUids.length][freqs.length]);
 
-        writeToFile(freqsLine(freqs) + uidLines(mUids, times));
+        setFreqsAndData(freqs, times);
         mReader.readDelta(mCallback);
         for (int i = 0; i < mUids.length; ++i) {
             mCallback.verify(mUids[i], times[i]);
@@ -155,14 +169,14 @@
         mCallback.verifyNoMoreInteractions();
 
         // Verify that readDelta also reads the frequencies if not already available.
-        assertTrue(mTestFile.delete());
+        clearFreqsAndData();
         long[] actualFreqs = mReader.readFreqs(mPowerProfile);
         assertArrayEquals(freqs, actualFreqs);
 
         // Verify that a second call will only return deltas.
         mCallback.clear();
         final long[][] newTimes1 = increaseTime(times);
-        writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes1));
+        setFreqsAndData(freqs, newTimes1);
         mReader.readDelta(mCallback);
         for (int i = 0; i < mUids.length; ++i) {
             mCallback.verify(mUids[i], subtract(newTimes1[i], times[i]));
@@ -177,7 +191,7 @@
         // Verify that calling with a null callback doesn't result in any crashes
         mCallback.clear();
         final long[][] newTimes2 = increaseTime(newTimes1);
-        writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes2));
+        setFreqsAndData(freqs, newTimes2);
         mReader.readDelta(null);
         mCallback.verifyNoMoreInteractions();
 
@@ -185,13 +199,13 @@
         // the previous call had null callback.
         mCallback.clear();
         final long[][] newTimes3 = increaseTime(newTimes2);
-        writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes3));
+        setFreqsAndData(freqs, newTimes3);
         mReader.readDelta(mCallback);
         for (int i = 0; i < mUids.length; ++i) {
             mCallback.verify(mUids[i], subtract(newTimes3[i], newTimes2[i]));
         }
         mCallback.verifyNoMoreInteractions();
-        assertTrue(mTestFile.delete());
+        clearFreqsAndData();
     }
 
     @Test
@@ -199,7 +213,7 @@
         final long[] freqs = {110, 123, 145, 167, 289, 997};
         final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
 
-        writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+        setFreqsAndData(freqs, times1);
         mReader.readAbsolute(mCallback);
         for (int i = 0; i < mUids.length; i++) {
             mCallback.verify(mUids[i], times1[i]);
@@ -207,20 +221,20 @@
         mCallback.verifyNoMoreInteractions();
 
         // Verify that readDelta also reads the frequencies if not already available.
-        assertTrue(mTestFile.delete());
+        clearFreqsAndData();
         long[] actualFreqs = mReader.readFreqs(mPowerProfile);
         assertArrayEquals(freqs, actualFreqs);
 
         // Verify that a second call should still return absolute values
         mCallback.clear();
         final long[][] times2 = increaseTime(times1);
-        writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+        setFreqsAndData(freqs, times2);
         mReader.readAbsolute(mCallback);
         for (int i = 0; i < mUids.length; i++) {
             mCallback.verify(mUids[i], times2[i]);
         }
         mCallback.verifyNoMoreInteractions();
-        assertTrue(mTestFile.delete());
+        clearFreqsAndData();
     }
 
     @Test
@@ -228,14 +242,14 @@
         final long[] freqs = {110, 123, 145, 167, 289, 997};
         final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
 
-        writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+        setFreqsAndData(freqs, times1);
         mReader.readDelta(mCallback);
 
         // Verify that there should not be a callback for a particular UID if its time decreases.
         mCallback.clear();
         final long[][] times2 = increaseTime(times1);
         times2[0][0] = 1000;
-        writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+        setFreqsAndData(freqs, times2);
         mReader.readDelta(mCallback);
         for (int i = 1; i < mUids.length; i++) {
             mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
@@ -246,7 +260,7 @@
         mCallback.clear();
         final long[][] times3 = increaseTime(times2);
         times3[0] = increaseTime(times1)[0];
-        writeToFile(freqsLine(freqs) + uidLines(mUids, times3));
+        setFreqsAndData(freqs, times3);
         mReader.readDelta(mCallback);
         mCallback.verify(mUids[0], subtract(times3[0], times1[0]));
         for (int i = 1; i < mUids.length; i++) {
@@ -258,7 +272,7 @@
         mCallback.clear();
         final long[][] times4 = increaseTime(times3);
         times4[0][0] *= -1;
-        writeToFile(freqsLine(freqs) + uidLines(mUids, times4));
+        setFreqsAndData(freqs, times4);
         mReader.readDelta(mCallback);
         for (int i = 1; i < mUids.length; ++i) {
             mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
@@ -269,14 +283,44 @@
         mCallback.clear();
         final long[][] times5 = increaseTime(times4);
         times5[0] = increaseTime(times3)[0];
-        writeToFile(freqsLine(freqs) + uidLines(mUids, times5));
+        setFreqsAndData(freqs, times5);
         mReader.readDelta(mCallback);
         mCallback.verify(mUids[0], subtract(times5[0], times3[0]));
         for (int i = 1; i < mUids.length; i++) {
             mCallback.verify(mUids[i], subtract(times5[i], times4[i]));
         }
 
-        assertTrue(mTestFile.delete());
+        clearFreqsAndData();
+    }
+
+    private void setFreqs(long[] freqs) throws IOException {
+        if (mUseBpf) {
+            mBpfMapReader.setFreqs(freqs);
+        } else {
+            writeToFile(freqsLine(freqs));
+        }
+    }
+
+    private void setFreqsAndData(long[] freqs, long[][] times) throws IOException {
+        if (mUseBpf) {
+            mBpfMapReader.setFreqs(freqs);
+            SparseArray<long[]> data = new SparseArray<>();
+            for (int i = 0; i < mUids.length; i++) {
+                data.put(mUids[i], times[i]);
+            }
+            mBpfMapReader.setData(data);
+        } else {
+            writeToFile(freqsLine(freqs) + uidLines(mUids, times));
+        }
+    }
+
+    private void clearFreqsAndData() {
+        if (mUseBpf) {
+            mBpfMapReader.setFreqs(null);
+            mBpfMapReader.setData(new SparseArray<>());
+        } else {
+            assertTrue(mTestFile.delete());
+        }
     }
 
     private String freqsLine(long[] freqs) {
@@ -358,4 +402,36 @@
             assertEquals(0, mData.size());
         }
     }
+
+    private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
+        private long[] mCpuFreqs;
+        private SparseArray<long[]> mNewData = new SparseArray<>();
+
+        public void setData(SparseArray<long[]> data) {
+            mNewData = data;
+        }
+
+        public void setFreqs(long[] freqs) {
+            mCpuFreqs = freqs;
+        }
+
+        @Override
+        public final boolean startTrackingBpfTimes() {
+            return true;
+        }
+
+        @Override
+        protected final boolean readBpfData() {
+            if (!mUseBpf) {
+                return false;
+            }
+            mData = mNewData;
+            return true;
+        }
+
+        @Override
+        public final long[] getDataDimensions() {
+            return mCpuFreqs;
+        }
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
index 479e19e..dac35e5 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
@@ -31,20 +31,33 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.Arrays;
+import java.util.Collection;
 
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 public class KernelSingleUidTimeReaderTest {
     private final static int TEST_UID = 2222;
     private final static int TEST_FREQ_COUNT = 5;
 
     private KernelSingleUidTimeReader mReader;
     private TestInjector mInjector;
+    protected boolean mUseBpf;
+
+    @Parameters(name="useBpf={0}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] { {true}, {false} });
+    }
+
+    public KernelSingleUidTimeReaderTest(boolean useBpf) {
+        mUseBpf = useBpf;
+    }
 
     @Before
     public void setUp() {
@@ -273,6 +286,7 @@
 
     class TestInjector extends Injector {
         private byte[] mData;
+        private long[] mBpfData;
         private boolean mThrowExcpetion;
 
         @Override
@@ -284,6 +298,14 @@
             }
         }
 
+        @Override
+        public long[] readBpfData(int uid) {
+            if (!mUseBpf || mBpfData == null) {
+                return new long[0];
+            }
+            return mBpfData;
+        }
+
         public void setData(long[] cpuTimes) {
             final ByteBuffer buffer = ByteBuffer.allocate(cpuTimes.length * Long.BYTES);
             buffer.order(ByteOrder.nativeOrder());
@@ -291,6 +313,7 @@
                 buffer.putLong(time / 10);
             }
             mData = buffer.array();
+            mBpfData = cpuTimes.clone();
         }
 
         public void letReadDataThrowException(boolean throwException) {
diff --git a/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java b/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
new file mode 100644
index 0000000..c4080e8
--- /dev/null
+++ b/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.app.PropertyInvalidatedCache;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+public class PropertyInvalidatedCacheTest extends TestCase {
+    private static final String KEY = "sys.testkey";
+    private static final String UNSET_KEY = "Aiw7woh6ie4toh7W";
+
+    private static class TestCache extends PropertyInvalidatedCache<Integer, String> {
+        TestCache() {
+            this(KEY);
+        }
+
+        TestCache(String key) {
+            super(4, key);
+        }
+
+        @Override
+        protected String recompute(Integer qv) {
+            mRecomputeCount += 1;
+            return "foo" + qv.toString();
+        }
+
+        int getRecomputeCount() {
+            return mRecomputeCount;
+        }
+
+        private int mRecomputeCount = 0;
+    }
+
+    @Override
+    protected void setUp() {
+        SystemProperties.set(KEY, "");
+    }
+
+    @SmallTest
+    public void testCacheRecompute() throws Exception {
+        TestCache cache = new TestCache();
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals("foo5", cache.query(5));
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals("foo6", cache.query(6));
+        assertEquals(2, cache.getRecomputeCount());
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(3, cache.getRecomputeCount());
+    }
+
+    @SmallTest
+    public void testCacheInitialState() throws Exception {
+        TestCache cache = new TestCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(2, cache.getRecomputeCount());
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(3, cache.getRecomputeCount());
+    }
+
+    @SmallTest
+    public void testCachePropertyUnset() throws Exception {
+        TestCache cache = new TestCache(UNSET_KEY);
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(2, cache.getRecomputeCount());
+    }
+
+    @SmallTest
+    public void testCacheDisableState() throws Exception {
+        TestCache cache = new TestCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(2, cache.getRecomputeCount());
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(3, cache.getRecomputeCount());
+        cache.disableSystemWide();
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(5, cache.getRecomputeCount());
+        cache.invalidateCache();  // Should not reenable
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(7, cache.getRecomputeCount());
+    }
+
+    @SmallTest
+    public void testRefreshSameObject() throws Exception {
+        int[] refreshCount = new int[1];
+        TestCache cache = new TestCache() {
+            @Override
+            protected String refresh(String oldResult, Integer query) {
+                refreshCount[0] += 1;
+                return oldResult;
+            }
+        };
+        cache.invalidateCache();
+        String result1 = cache.query(5);
+        assertEquals("foo5", result1);
+        String result2 = cache.query(5);
+        assertSame(result1, result2);
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals(1, refreshCount[0]);
+        assertEquals("foo5", cache.query(5));
+        assertEquals(2, refreshCount[0]);
+    }
+
+    @SmallTest
+    public void testRefreshInvalidateRace() throws Exception {
+        int[] refreshCount = new int[1];
+        TestCache cache = new TestCache() {
+            @Override
+            protected String refresh(String oldResult, Integer query) {
+                refreshCount[0] += 1;
+                invalidateCache();
+                return new String(oldResult);
+            }
+        };
+        cache.invalidateCache();
+        String result1 = cache.query(5);
+        assertEquals("foo5", result1);
+        String result2 = cache.query(5);
+        assertEquals(result1, result2);
+        assertNotSame(result1, result2);
+        assertEquals(2, cache.getRecomputeCount());
+    }
+
+    @SmallTest
+    public void testLocalProcessDisable() throws Exception {
+        TestCache cache = new TestCache();
+        cache.invalidateCache();
+        assertEquals("foo5", cache.query(5));
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals("foo5", cache.query(5));
+        assertEquals(1, cache.getRecomputeCount());
+        assertEquals(cache.isDisabledLocal(), false);
+        cache.disableLocal();
+        assertEquals(cache.isDisabledLocal(), true);
+        assertEquals("foo5", cache.query(5));
+        assertEquals("foo5", cache.query(5));
+        assertEquals(3, cache.getRecomputeCount());
+    }
+
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 821909d..162dcc9 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -26,6 +26,10 @@
         <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.angle">
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+
     <privapp-permissions package="com.android.apps.tag">
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
     </privapp-permissions>
@@ -377,6 +381,7 @@
     <privapp-permissions package="com.android.dynsystem">
         <permission name="android.permission.REBOOT"/>
         <permission name="android.permission.MANAGE_DYNAMIC_SYSTEM"/>
+        <permission name="android.permission.READ_OEM_UNLOCK_STATE"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.settings">
diff --git a/libs/androidfw/tests/CommonHelpers.h b/libs/androidfw/tests/CommonHelpers.h
index 8af13f2..316a57a 100644
--- a/libs/androidfw/tests/CommonHelpers.h
+++ b/libs/androidfw/tests/CommonHelpers.h
@@ -21,8 +21,6 @@
 #include <string>
 
 #include "androidfw/ResourceTypes.h"
-#include "utils/String16.h"
-#include "utils/String8.h"
 
 namespace android {
 
@@ -40,10 +38,6 @@
   return a.compare(b) == 0;
 }
 
-static inline ::std::ostream& operator<<(::std::ostream& out, const String8& str) {
-  return out << str.string();
-}
-
 static inline ::std::ostream& operator<<(::std::ostream& out, const ResTable_config& c) {
   return out << c.toString();
 }
diff --git a/libs/usb/Android.bp b/libs/usb/Android.bp
index 027a748..e752b55 100644
--- a/libs/usb/Android.bp
+++ b/libs/usb/Android.bp
@@ -19,5 +19,3 @@
     srcs: ["src/**/*.java"],
     api_packages: ["com.android.future.usb"],
 }
-
-subdirs = ["tests/*"]
diff --git a/libs/usb/tests/AccessoryChat/Android.bp b/libs/usb/tests/AccessoryChat/Android.bp
index 63a670c..19ed3d3 100644
--- a/libs/usb/tests/AccessoryChat/Android.bp
+++ b/libs/usb/tests/AccessoryChat/Android.bp
@@ -1,4 +1,3 @@
-subdirs = ["accessorychat"]
 //
 // Copyright (C) 2011 The Android Open Source Project
 //
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 6fd47c4..66a59e0 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -509,15 +509,19 @@
      * @return The position of the {@link Uri}, or -1 if it cannot be found.
      */
     public int getRingtonePosition(Uri ringtoneUri) {
-        if (ringtoneUri == null) return -1;
-        final long ringtoneId = ContentUris.parseId(ringtoneUri);
-        
-        final Cursor cursor = getCursor();
-        cursor.moveToPosition(-1);
-        while (cursor.moveToNext()) {
-            if (ringtoneId == cursor.getLong(ID_COLUMN_INDEX)) {
-                return cursor.getPosition();
+        try {
+            if (ringtoneUri == null) return -1;
+            final long ringtoneId = ContentUris.parseId(ringtoneUri);
+
+            final Cursor cursor = getCursor();
+            cursor.moveToPosition(-1);
+            while (cursor.moveToNext()) {
+                if (ringtoneId == cursor.getLong(ID_COLUMN_INDEX)) {
+                    return cursor.getPosition();
+                }
             }
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "NumberFormatException while getting ringtone position, returning -1", e);
         }
         return -1;
     }
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index d4c4a62..7d19985 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -58,6 +59,8 @@
     private final String mId;
     private final String mLanguage;
     private final CharSequence mDescription;
+    @Nullable
+    private final String mEncoding;
     private final boolean mEncrypted;
     private final int mAudioChannelCount;
     private final int mAudioSampleRate;
@@ -73,14 +76,15 @@
     private final Bundle mExtra;
 
     private TvTrackInfo(int type, String id, String language, CharSequence description,
-            boolean encrypted, int audioChannelCount, int audioSampleRate, boolean audioDescription,
-            boolean hardOfHearing, boolean spokenSubtitle, int videoWidth, int videoHeight,
-            float videoFrameRate, float videoPixelAspectRatio, byte videoActiveFormatDescription,
-            Bundle extra) {
+            String encoding, boolean encrypted, int audioChannelCount, int audioSampleRate,
+            boolean audioDescription, boolean hardOfHearing, boolean spokenSubtitle, int videoWidth,
+            int videoHeight, float videoFrameRate, float videoPixelAspectRatio,
+            byte videoActiveFormatDescription, Bundle extra) {
         mType = type;
         mId = id;
         mLanguage = language;
         mDescription = description;
+        mEncoding = encoding;
         mEncrypted = encrypted;
         mAudioChannelCount = audioChannelCount;
         mAudioSampleRate = audioSampleRate;
@@ -100,6 +104,7 @@
         mId = in.readString();
         mLanguage = in.readString();
         mDescription = in.readString();
+        mEncoding = in.readString();
         mEncrypted = in.readInt() != 0;
         mAudioChannelCount = in.readInt();
         mAudioSampleRate = in.readInt();
@@ -146,13 +151,26 @@
     }
 
     /**
+     * Returns the codec in the form of mime type. If the encoding is unknown or could not be
+     * determined, the corresponding value will be {@code null}.
+     *
+     * <p>For example of broadcast, codec information may be referred to broadcast standard (e.g.
+     * Component Descriptor of ETSI EN 300 468). In the case that track type is subtitle, mime type
+     * could be defined in broadcast standard (e.g. "text/dvb.subtitle" or "text/dvb.teletext" in
+     * ETSI TS 102 812 V1.3.1 section 7.6).
+     */
+    @Nullable
+    public String getEncoding() {
+        return mEncoding;
+    }
+
+    /**
      * Returns {@code true} if the track is encrypted, {@code false} otherwise. If the encryption
      * status is unknown or could not be determined, the corresponding value will be {@code false}.
      *
      * <p>For example: ISO/IEC 13818-1 defines a CA descriptor that can be used to determine the
      * encryption status of some broadcast streams.
      */
-
     public boolean isEncrypted() {
         return mEncrypted;
     }
@@ -323,6 +341,7 @@
         dest.writeString(mId);
         dest.writeString(mLanguage);
         dest.writeString(mDescription != null ? mDescription.toString() : null);
+        dest.writeString(mEncoding);
         dest.writeInt(mEncrypted ? 1 : 0);
         dest.writeInt(mAudioChannelCount);
         dest.writeInt(mAudioSampleRate);
@@ -352,6 +371,7 @@
         if (!TextUtils.equals(mId, obj.mId) || mType != obj.mType
                 || !TextUtils.equals(mLanguage, obj.mLanguage)
                 || !TextUtils.equals(mDescription, obj.mDescription)
+                || !TextUtils.equals(mEncoding, obj.mEncoding)
                 || mEncrypted != obj.mEncrypted) {
             return false;
         }
@@ -413,6 +433,7 @@
         private final int mType;
         private String mLanguage;
         private CharSequence mDescription;
+        private String mEncoding;
         private boolean mEncrypted;
         private int mAudioChannelCount;
         private int mAudioSampleRate;
@@ -468,6 +489,22 @@
         }
 
         /**
+         * Sets the encoding of the track.
+         *
+         * <p>For example of broadcast, codec information may be referred to broadcast standard
+         * (e.g. Component Descriptor of ETSI EN 300 468). In the case that track type is subtitle,
+         * mime type could be defined in broadcast standard (e.g. "text/dvb.subtitle" or
+         * "text/dvb.teletext" in ETSI TS 102 812 V1.3.1 section 7.6).
+         *
+         * @param encoding The encoding of the track in the form of mime type.
+         */
+        @NonNull
+        public Builder setEncoding(@Nullable String encoding) {
+            mEncoding = encoding;
+            return this;
+        }
+
+        /**
          * Sets the encryption status of the track.
          *
          * <p>For example: ISO/IEC 13818-1 defines a CA descriptor that can be used to determine the
@@ -672,7 +709,7 @@
          * @return The new {@link TvTrackInfo} instance
          */
         public TvTrackInfo build() {
-            return new TvTrackInfo(mType, mId, mLanguage, mDescription, mEncrypted,
+            return new TvTrackInfo(mType, mId, mLanguage, mDescription, mEncoding, mEncrypted,
                     mAudioChannelCount, mAudioSampleRate, mAudioDescription, mHardOfHearing,
                     mSpokenSubtitle, mVideoWidth, mVideoHeight, mVideoFrameRate,
                     mVideoPixelAspectRatio, mVideoActiveFormatDescription, mExtra);
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index f873eeb..27660db 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -124,8 +124,3 @@
         "-Wunreachable-code",
     ],
 }
-
-subdirs = [
-    "audioeffect",
-    "soundpool",
-]
diff --git a/media/native/Android.bp b/media/native/Android.bp
deleted file mode 100644
index b44c296..0000000
--- a/media/native/Android.bp
+++ /dev/null
@@ -1 +0,0 @@
-subdirs = ["*"]
diff --git a/packages/DynamicSystemInstallationService/AndroidManifest.xml b/packages/DynamicSystemInstallationService/AndroidManifest.xml
index d718eae9..b4d520d 100644
--- a/packages/DynamicSystemInstallationService/AndroidManifest.xml
+++ b/packages/DynamicSystemInstallationService/AndroidManifest.xml
@@ -7,6 +7,7 @@
     <uses-permission android:name="android.permission.MANAGE_DYNAMIC_SYSTEM" />
     <uses-permission android:name="android.permission.REBOOT" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.READ_OEM_UNLOCK_STATE" />
 
     <application
         android:allowBackup="false"
diff --git a/packages/DynamicSystemInstallationService/res/values/strings.xml b/packages/DynamicSystemInstallationService/res/values/strings.xml
index 7595d2b..e124be6 100644
--- a/packages/DynamicSystemInstallationService/res/values/strings.xml
+++ b/packages/DynamicSystemInstallationService/res/values/strings.xml
@@ -18,6 +18,8 @@
     <string name="notification_install_inprogress">Install in progress</string>
     <!-- Displayed on notification: Dynamic System installation failed [CHAR LIMIT=128] -->
     <string name="notification_install_failed">Install failed</string>
+    <!-- Displayed on notification: Image validation failed [CHAR LIMIT=128] -->
+    <string name="notification_image_validation_failed">Image validation failed. Abort installation.</string>
     <!-- Displayed on notification: We are running in Dynamic System [CHAR LIMIT=128] -->
     <string name="notification_dynsystem_in_use">Currently running a dynamic system. Restart to use the original Android version.</string>
 
@@ -25,10 +27,11 @@
     <string name="notification_action_cancel">Cancel</string>
     <!-- Action on notification: Discard installation [CHAR LIMIT=16] -->
     <string name="notification_action_discard">Discard</string>
-    <!-- Action on notification: Uninstall Dynamic System [CHAR LIMIT=16] -->
-    <string name="notification_action_uninstall">Uninstall</string>
     <!-- Action on notification: Restart to Dynamic System [CHAR LIMIT=16] -->
     <string name="notification_action_reboot_to_dynsystem">Restart</string>
+    <!-- Action on notification: Restart to original Android version [CHAR LIMIT=16] -->
+    <string name="notification_action_reboot_to_origin">Restart</string>
+
 
     <!-- Toast when installed Dynamic System is discarded [CHAR LIMIT=64] -->
     <string name="toast_dynsystem_discarded">Discarded dynamic system</string>
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 9bae223..f36f97d 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -80,6 +80,10 @@
     static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
     static final String KEY_DSU_SLOT = "KEY_DSU_SLOT";
     static final String DEFAULT_DSU_SLOT = "dsu";
+    static final String KEY_PUBKEY = "KEY_PUBKEY";
+
+    // Default userdata partition size is 2GiB.
+    private static final long DEFAULT_USERDATA_SIZE = 2L << 30;
 
     /*
      * Intent actions
@@ -267,6 +271,11 @@
         long userdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0);
         mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false);
         String dsuSlot = intent.getStringExtra(KEY_DSU_SLOT);
+        String publicKey = intent.getStringExtra(KEY_PUBKEY);
+
+        if (userdataSize == 0) {
+            userdataSize = DEFAULT_USERDATA_SIZE;
+        }
 
         if (TextUtils.isEmpty(dsuSlot)) {
             dsuSlot = DEFAULT_DSU_SLOT;
@@ -274,7 +283,7 @@
         // TODO: better constructor or builder
         mInstallTask =
                 new InstallationAsyncTask(
-                        url, dsuSlot, systemSize, userdataSize, this, mDynSystem, this);
+                        url, dsuSlot, publicKey, systemSize, userdataSize, this, mDynSystem, this);
 
         mInstallTask.execute();
 
@@ -408,6 +417,10 @@
     }
 
     private Notification buildNotification(int status, int cause) {
+        return buildNotification(status, cause, null);
+    }
+
+    private Notification buildNotification(int status, int cause, Throwable detail) {
         Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
                 .setSmallIcon(R.drawable.ic_system_update_googblue_24dp)
                 .setProgress(0, 0, false);
@@ -456,14 +469,19 @@
                         .setStyle(new Notification.BigTextStyle().bigText(msgInUse));
 
                 builder.addAction(new Notification.Action.Builder(
-                        null, getString(R.string.notification_action_uninstall),
+                        null, getString(R.string.notification_action_reboot_to_origin),
                         createPendingIntent(ACTION_REBOOT_TO_NORMAL)).build());
 
                 break;
 
             case STATUS_NOT_STARTED:
                 if (cause != CAUSE_NOT_SPECIFIED && cause != CAUSE_INSTALL_CANCELLED) {
-                    builder.setContentText(getString(R.string.notification_install_failed));
+                    if (detail instanceof InstallationAsyncTask.ImageValidationException) {
+                        builder.setContentText(
+                                getString(R.string.notification_image_validation_failed));
+                    } else {
+                        builder.setContentText(getString(R.string.notification_install_failed));
+                    }
                 } else {
                     // no need to notify the user if the task is not started, or cancelled.
                 }
@@ -525,7 +543,7 @@
                 break;
         }
 
-        Log.d(TAG, "status=" + statusString + ", cause=" + causeString);
+        Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail);
 
         boolean notifyOnNotificationBar = true;
 
@@ -538,7 +556,7 @@
         }
 
         if (notifyOnNotificationBar) {
-            mNM.notify(NOTIFICATION_ID, buildNotification(status, cause));
+            mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail));
         }
 
         for (int i = mClients.size() - 1; i >= 0; i--) {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 438c435..f8952ac 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -17,11 +17,13 @@
 package com.android.dynsystem;
 
 import android.content.Context;
+import android.gsi.AvbPublicKey;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.MemoryFile;
 import android.os.ParcelFileDescriptor;
 import android.os.image.DynamicSystemManager;
+import android.service.persistentdata.PersistentDataBlockManager;
 import android.util.Log;
 import android.webkit.URLUtil;
 
@@ -51,18 +53,46 @@
     private static final List<String> UNSUPPORTED_PARTITIONS =
             Arrays.asList("vbmeta", "boot", "userdata", "dtbo", "super_empty", "system_other");
 
-    private class UnsupportedUrlException extends RuntimeException {
+    private class UnsupportedUrlException extends Exception {
         private UnsupportedUrlException(String message) {
             super(message);
         }
     }
 
-    private class UnsupportedFormatException extends RuntimeException {
+    private class UnsupportedFormatException extends Exception {
         private UnsupportedFormatException(String message) {
             super(message);
         }
     }
 
+    static class ImageValidationException extends Exception {
+        ImageValidationException(String message) {
+            super(message);
+        }
+
+        ImageValidationException(Throwable cause) {
+            super(cause);
+        }
+    }
+
+    static class RevocationListFetchException extends ImageValidationException {
+        RevocationListFetchException(Throwable cause) {
+            super(cause);
+        }
+    }
+
+    static class KeyRevokedException extends ImageValidationException {
+        KeyRevokedException(String message) {
+            super(message);
+        }
+    }
+
+    static class PublicKeyException extends ImageValidationException {
+        PublicKeyException(String message) {
+            super(message);
+        }
+    }
+
     /** UNSET means the installation is not completed */
     static final int RESULT_UNSET = 0;
     static final int RESULT_OK = 1;
@@ -97,12 +127,14 @@
 
     private final String mUrl;
     private final String mDsuSlot;
+    private final String mPublicKey;
     private final long mSystemSize;
     private final long mUserdataSize;
     private final Context mContext;
     private final DynamicSystemManager mDynSystem;
     private final ProgressListener mListener;
     private final boolean mIsNetworkUrl;
+    private final boolean mIsDeviceBootloaderUnlocked;
     private DynamicSystemManager.Session mInstallationSession;
     private KeyRevocationList mKeyRevocationList;
 
@@ -115,6 +147,7 @@
     InstallationAsyncTask(
             String url,
             String dsuSlot,
+            String publicKey,
             long systemSize,
             long userdataSize,
             Context context,
@@ -122,12 +155,20 @@
             ProgressListener listener) {
         mUrl = url;
         mDsuSlot = dsuSlot;
+        mPublicKey = publicKey;
         mSystemSize = systemSize;
         mUserdataSize = userdataSize;
         mContext = context;
         mDynSystem = dynSystem;
         mListener = listener;
         mIsNetworkUrl = URLUtil.isNetworkUrl(mUrl);
+        PersistentDataBlockManager pdbManager =
+                (PersistentDataBlockManager)
+                        mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
+        mIsDeviceBootloaderUnlocked =
+                (pdbManager != null)
+                        && (pdbManager.getFlashLockState()
+                                == PersistentDataBlockManager.FLASH_LOCK_UNLOCKED);
     }
 
     @Override
@@ -157,8 +198,6 @@
                 return null;
             }
 
-            // TODO(yochiang): do post-install public key check (revocation list / boot-ramdisk)
-
             mDynSystem.finishInstallation();
         } catch (Exception e) {
             Log.e(TAG, e.toString(), e);
@@ -242,23 +281,26 @@
                     String.format(Locale.US, "Unsupported URL: %s", mUrl));
         }
 
-        // TODO(yochiang): Bypass this check if device is unlocked
         try {
             String listUrl = mContext.getString(R.string.key_revocation_list_url);
             mKeyRevocationList = KeyRevocationList.fromUrl(new URL(listUrl));
         } catch (IOException | JSONException e) {
-            Log.d(TAG, "Failed to fetch Dynamic System Key Revocation List");
             mKeyRevocationList = new KeyRevocationList();
-            keyRevocationThrowOrWarning(e);
+            imageValidationThrowOrWarning(new RevocationListFetchException(e));
+        }
+        if (mKeyRevocationList.isRevoked(mPublicKey)) {
+            imageValidationThrowOrWarning(new KeyRevokedException(mPublicKey));
         }
     }
 
-    private void keyRevocationThrowOrWarning(Exception e) throws Exception {
-        if (mIsNetworkUrl) {
-            throw e;
-        } else {
-            // If DSU is being installed from a local file URI, then be permissive
+    private void imageValidationThrowOrWarning(ImageValidationException e)
+            throws ImageValidationException {
+        if (mIsDeviceBootloaderUnlocked || !mIsNetworkUrl) {
+            // If device is OEM unlocked or DSU is being installed from a local file URI,
+            // then be permissive.
             Log.w(TAG, e.toString());
+        } else {
+            throw e;
         }
     }
 
@@ -294,7 +336,8 @@
         }
     }
 
-    private void installImages() throws IOException, InterruptedException {
+    private void installImages()
+            throws IOException, InterruptedException, ImageValidationException {
         if (mStream != null) {
             if (mIsZip) {
                 installStreamingZipUpdate();
@@ -306,12 +349,14 @@
         }
     }
 
-    private void installStreamingGzUpdate() throws IOException, InterruptedException {
+    private void installStreamingGzUpdate()
+            throws IOException, InterruptedException, ImageValidationException {
         Log.d(TAG, "To install a streaming GZ update");
         installImage("system", mSystemSize, new GZIPInputStream(mStream), 1);
     }
 
-    private void installStreamingZipUpdate() throws IOException, InterruptedException {
+    private void installStreamingZipUpdate()
+            throws IOException, InterruptedException, ImageValidationException {
         Log.d(TAG, "To install a streaming ZIP update");
 
         ZipInputStream zis = new ZipInputStream(mStream);
@@ -330,7 +375,8 @@
         }
     }
 
-    private void installLocalZipUpdate() throws IOException, InterruptedException {
+    private void installLocalZipUpdate()
+            throws IOException, InterruptedException, ImageValidationException {
         Log.d(TAG, "To install a local ZIP update");
 
         Enumeration<? extends ZipEntry> entries = mZipFile.entries();
@@ -349,8 +395,9 @@
         }
     }
 
-    private boolean installImageFromAnEntry(ZipEntry entry, InputStream is,
-            int numInstalledPartitions) throws IOException, InterruptedException {
+    private boolean installImageFromAnEntry(
+            ZipEntry entry, InputStream is, int numInstalledPartitions)
+            throws IOException, InterruptedException, ImageValidationException {
         String name = entry.getName();
 
         Log.d(TAG, "ZipEntry: " + name);
@@ -373,8 +420,9 @@
         return true;
     }
 
-    private void installImage(String partitionName, long uncompressedSize, InputStream is,
-            int numInstalledPartitions) throws IOException, InterruptedException {
+    private void installImage(
+            String partitionName, long uncompressedSize, InputStream is, int numInstalledPartitions)
+            throws IOException, InterruptedException, ImageValidationException {
 
         SparseInputStream sis = new SparseInputStream(new BufferedInputStream(is));
 
@@ -445,6 +493,24 @@
                 publishProgress(progress);
             }
         }
+
+        AvbPublicKey avbPublicKey = new AvbPublicKey();
+        if (!mInstallationSession.getAvbPublicKey(avbPublicKey)) {
+            imageValidationThrowOrWarning(new PublicKeyException("getAvbPublicKey() failed"));
+        } else {
+            String publicKey = toHexString(avbPublicKey.sha1);
+            if (mKeyRevocationList.isRevoked(publicKey)) {
+                imageValidationThrowOrWarning(new KeyRevokedException(publicKey));
+            }
+        }
+    }
+
+    private static String toHexString(byte[] bytes) {
+        StringBuilder sb = new StringBuilder();
+        for (byte b : bytes) {
+            sb.append(String.format("%02x", b));
+        }
+        return sb.toString();
     }
 
     private void close() {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
index e42ded7..82ea744 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
@@ -84,7 +84,6 @@
         // retrieve data from calling intent
         Intent callingIntent = getIntent();
         Uri url = callingIntent.getData();
-        Bundle extras = callingIntent.getExtras();
 
         if (url != null) {
             sVerifiedUrl = url.toString();
@@ -96,7 +95,7 @@
             intent.setData(url);
         }
         intent.setAction(DynamicSystemClient.ACTION_START_INSTALL);
-        intent.putExtras(extras);
+        intent.putExtras(callingIntent);
 
         Log.d(TAG, "Starting Installation Service");
         startServiceAsUser(intent, UserHandle.SYSTEM);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index e75ad13..6c1d2796 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1009,6 +1009,11 @@
                         String value = setting != null ? setting.getValue() : null;
                         updateGlobalSetting(Settings.Global.ADB_ENABLED,
                                 value, null, true, userId, true);
+
+                        setting = getGlobalSetting(Settings.Global.ADB_WIFI_ENABLED);
+                        value = setting != null ? setting.getValue() : null;
+                        updateGlobalSetting(Settings.Global.ADB_WIFI_ENABLED,
+                                value, null, true, userId, true);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(identity);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 262365da..a9fa2ac 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -430,6 +430,25 @@
             android:excludeFromRecents="true">
         </activity>
 
+        <!-- started from WirelessDebuggingManager -->
+        <activity android:name=".wifi.WifiDebuggingActivity"
+            android:permission="android.permission.MANAGE_DEBUGGING"
+            android:theme="@style/Theme.SystemUI.Dialog.Alert"
+            android:finishOnCloseSystemDialogs="true"
+            android:excludeFromRecents="true">
+        </activity>
+        <activity-alias
+            android:name=".WifiDebuggingActivityAlias"
+            android:permission="android.permission.DUMP"
+            android:targetActivity=".wifi.WifiDebuggingActivity"
+            android:exported="true">
+        </activity-alias>
+        <activity android:name=".wifi.WifiDebuggingSecondaryUserActivity"
+            android:theme="@style/Theme.SystemUI.Dialog.Alert"
+            android:finishOnCloseSystemDialogs="true"
+            android:excludeFromRecents="true">
+        </activity>
+
         <!-- started from NetworkPolicyManagerService -->
         <activity
             android:name=".net.NetworkOverLimitActivity"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d58f83f..f5ccc53 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -162,6 +162,24 @@
     <!-- Message of notification shown when trying to enable USB debugging but a secondary user is the current foreground user. -->
     <string name="usb_debugging_secondary_user_message">The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user.</string>
 
+    <!-- Title of confirmation dialog for wireless debugging [CHAR LIMIT=NONE] -->
+    <string name="wifi_debugging_title">Allow wireless debugging on this network?</string>
+
+    <!-- Message of confirmation dialog for wireless debugging [CHAR LIMIT=NONE] -->
+    <string name="wifi_debugging_message">Network Name (SSID)\n<xliff:g id="ssid" example="My wifi">%1$s</xliff:g>\n\nWi\u2011Fi Address (BSSID)\n<xliff:g id="bssid" example="AB:CD:EF:12:34:56">%2$s</xliff:g></string>
+
+    <!-- Option to always allow wireless debugging on this network [CHAR LIMIT=NONE] -->
+    <string name="wifi_debugging_always">Always allow on this network</string>
+
+    <!-- Button label for confirming acceptance of enabling wireless debugging [CHAR LIMIT=15] -->
+    <string name="wifi_debugging_allow">Allow</string>
+
+    <!-- Title of notification shown when trying to enable wireless debugging but a secondary user is the current foreground user. [CHAR LIMIT=NONE] -->
+    <string name="wifi_debugging_secondary_user_title">Wireless debugging not allowed</string>
+
+    <!-- Message of notification shown when trying to enable wireless debugging but a secondary user is the current foreground user. [CHAR LIMIT=NONE] -->
+    <string name="wifi_debugging_secondary_user_message">The user currently signed in to this device can\u2019t turn on wireless debugging. To use this feature, switch to the primary user.</string>
+
     <!-- Title of USB contaminant presence dialog [CHAR LIMIT=NONE] -->
     <string name="usb_contaminant_title">USB port disabled</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index b73aff4..e2cafdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -26,6 +26,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.provider.Settings.Global;
+import android.telephony.Annotation;
 import android.telephony.CellSignalStrength;
 import android.telephony.CellSignalStrengthCdma;
 import android.telephony.NetworkRegistrationInfo;
@@ -38,7 +39,6 @@
 import android.text.Html;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.TelephonyIntents;
@@ -54,7 +54,9 @@
 
 import java.io.PrintWriter;
 import java.util.BitSet;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -77,12 +79,14 @@
     final SubscriptionInfo mSubscriptionInfo;
 
     // @VisibleForDemoMode
-    final SparseArray<MobileIconGroup> mNetworkToIconLookup;
+    final Map<String, MobileIconGroup> mNetworkToIconLookup;
 
     // Since some pieces of the phone state are interdependent we store it locally,
     // this could potentially become part of MobileState for simplification/complication
     // of code.
     private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+    private boolean mCA = false;
+    private boolean mCAPlus = false;
     private int mDataState = TelephonyManager.DATA_DISCONNECTED;
     private ServiceState mServiceState;
     private SignalStrength mSignalStrength;
@@ -93,9 +97,6 @@
     boolean mInflateSignalStrengths = false;
     @VisibleForTesting
     boolean mIsShowingIconGracefully = false;
-    // Some specific carriers have 5GE network which is special LTE CA network.
-    private static final int NETWORK_TYPE_LTE_CA_5GE =
-            TelephonyManager.getAllNetworkTypes().length + 1;
 
     // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
     // need listener lists anymore.
@@ -106,7 +107,7 @@
         super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
                 NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
                 networkController);
-        mNetworkToIconLookup = new SparseArray<>();
+        mNetworkToIconLookup = new HashMap<>();
         mConfig = config;
         mPhone = phone;
         mDefaults = defaults;
@@ -210,29 +211,38 @@
     private void mapIconSets() {
         mNetworkToIconLookup.clear();
 
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyIcons.THREE_G);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyIcons.THREE_G);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_TD_SCDMA, TelephonyIcons.THREE_G);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_0),
+                TelephonyIcons.THREE_G);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_A),
+                TelephonyIcons.THREE_G);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_B),
+                TelephonyIcons.THREE_G);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EHRPD),
+                TelephonyIcons.THREE_G);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UMTS),
+                TelephonyIcons.THREE_G);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_TD_SCDMA),
+                TelephonyIcons.THREE_G);
 
         if (!mConfig.showAtLeast3G) {
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UNKNOWN),
                     TelephonyIcons.UNKNOWN);
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, TelephonyIcons.E);
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, TelephonyIcons.ONE_X);
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyIcons.ONE_X);
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EDGE),
+                    TelephonyIcons.E);
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_CDMA),
+                    TelephonyIcons.ONE_X);
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_1xRTT),
+                    TelephonyIcons.ONE_X);
 
             mDefaultIcons = TelephonyIcons.G;
         } else {
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UNKNOWN),
                     TelephonyIcons.THREE_G);
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE,
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EDGE),
                     TelephonyIcons.THREE_G);
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA,
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_CDMA),
                     TelephonyIcons.THREE_G);
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT,
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_1xRTT),
                     TelephonyIcons.THREE_G);
             mDefaultIcons = TelephonyIcons.THREE_G;
         }
@@ -247,33 +257,59 @@
             hPlusGroup = TelephonyIcons.H_PLUS;
         }
 
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hPlusGroup);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSDPA), hGroup);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSUPA), hGroup);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSPA), hGroup);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSPAP), hPlusGroup);
 
         if (mConfig.show4gForLte) {
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G);
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE),
+                    TelephonyIcons.FOUR_G);
             if (mConfig.hideLtePlus) {
-                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+                mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
                         TelephonyIcons.FOUR_G);
             } else {
-                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+                mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
                         TelephonyIcons.FOUR_G_PLUS);
             }
         } else {
-            mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.LTE);
+            mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE),
+                    TelephonyIcons.LTE);
             if (mConfig.hideLtePlus) {
-                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+                mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
                         TelephonyIcons.LTE);
             } else {
-                mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+                mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
                         TelephonyIcons.LTE_PLUS);
             }
         }
-        mNetworkToIconLookup.put(NETWORK_TYPE_LTE_CA_5GE,
+        mNetworkToIconLookup.put(toIconKeyCAPlus(TelephonyManager.NETWORK_TYPE_LTE),
                 TelephonyIcons.LTE_CA_5G_E);
-        mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_IWLAN, TelephonyIcons.WFC);
+        mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_IWLAN),
+                TelephonyIcons.WFC);
+    }
+
+    private String getIconKey() {
+        if (mCA) {
+            return toIconKeyCA(mDataNetType);
+        } else if (mCAPlus) {
+            return toIconKeyCAPlus(mDataNetType);
+        } else {
+            return toIconKey(mDataNetType);
+        }
+    }
+
+    // Some specific carriers have 5GE network which is special CA network.
+    private String toIconKeyCAPlus(@Annotation.NetworkType int networkType) {
+        return toIconKeyCA(networkType) + "_Plus";
+    }
+
+    private String toIconKeyCA(@Annotation.NetworkType int networkType) {
+        return toIconKey(networkType) + "_CA";
+    }
+
+    private String toIconKey(@Annotation.NetworkType int networkType) {
+        return Integer.toString(networkType);
     }
 
     private void updateInflateSignalStrength() {
@@ -527,10 +563,11 @@
             nr5GIconGroup = adjustNr5GIconGroupByDisplayGraceTime(nr5GIconGroup);
         }
 
+        String iconKey = getIconKey();
         if (nr5GIconGroup != null) {
             mCurrentState.iconGroup = nr5GIconGroup;
-        } else if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) {
-            mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType);
+        } else if (mNetworkToIconLookup.get(iconKey) != null) {
+            mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey);
         } else {
             mCurrentState.iconGroup = mDefaultIcons;
         }
@@ -683,6 +720,8 @@
         pw.println("  mSignalStrength=" + mSignalStrength + ",");
         pw.println("  mDataState=" + mDataState + ",");
         pw.println("  mDataNetType=" + mDataNetType + ",");
+        pw.println("  mCA=" + mCA + ",");
+        pw.println("  mCAPlus=" + mCAPlus + ",");
         pw.println("  mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
         pw.println("  isDataDisabled=" + isDataDisabled() + ",");
         pw.println("  mIsShowingIconGracefully=" + mIsShowingIconGracefully + ",");
@@ -733,11 +772,13 @@
 
         private void updateDataNetType(int networkType) {
             mDataNetType = networkType;
+            mCA = false;
+            mCAPlus = false;
             if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE) {
                 if (isCarrierSpecificDataIcon()) {
-                    mDataNetType = NETWORK_TYPE_LTE_CA_5GE;
+                    mCAPlus = true;
                 } else if (mServiceState != null && mServiceState.isUsingCarrierAggregation()) {
-                    mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA;
+                    mCA = true;
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java
new file mode 100644
index 0000000..a94af24
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wifi;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.debug.IAdbManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.EventLog;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.Toast;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import com.android.systemui.R;
+
+/**
+ * Alerts the user of an untrusted network when enabling wireless debugging.
+ * The user can either deny, allow, or allow with the "always allow on this
+ * network" checked.
+ */
+public class WifiDebuggingActivity extends AlertActivity
+                                  implements DialogInterface.OnClickListener {
+    private static final String TAG = "WifiDebuggingActivity";
+
+    private CheckBox mAlwaysAllow;
+    // Notifies when wifi is disabled, or the network changed
+    private WifiChangeReceiver mWifiChangeReceiver;
+    private WifiManager mWifiManager;
+    private String mBssid;
+    private boolean mClicked = false;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        Window window = getWindow();
+        window.addSystemFlags(
+                WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+        window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+
+        super.onCreate(icicle);
+
+
+        mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+        mWifiChangeReceiver = new WifiChangeReceiver(this);
+
+        Intent intent = getIntent();
+        String ssid = intent.getStringExtra("ssid");
+        mBssid = intent.getStringExtra("bssid");
+
+        if (ssid == null || mBssid == null) {
+            finish();
+            return;
+        }
+
+        final AlertController.AlertParams ap = mAlertParams;
+        ap.mTitle = getString(R.string.wifi_debugging_title);
+        ap.mMessage = getString(R.string.wifi_debugging_message, ssid, mBssid);
+        ap.mPositiveButtonText = getString(R.string.wifi_debugging_allow);
+        ap.mNegativeButtonText = getString(android.R.string.cancel);
+        ap.mPositiveButtonListener = this;
+        ap.mNegativeButtonListener = this;
+
+        // add "always allow" checkbox
+        LayoutInflater inflater = LayoutInflater.from(ap.mContext);
+        View checkbox = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+        mAlwaysAllow = (CheckBox) checkbox.findViewById(com.android.internal.R.id.alwaysUse);
+        mAlwaysAllow.setText(getString(R.string.wifi_debugging_always));
+        ap.mView = checkbox;
+        window.setCloseOnTouchOutside(false);
+
+        setupAlert();
+
+        // adding touch listener on affirmative button - checks if window is obscured
+        // if obscured, do not let user give permissions (could be tapjacking involved)
+        final View.OnTouchListener filterTouchListener = (View v, MotionEvent event) -> {
+            // Filter obscured touches by consuming them.
+            if (((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0)
+                    || ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0)) {
+                if (event.getAction() == MotionEvent.ACTION_UP) {
+                    // TODO: need a different value for safety net?
+                    EventLog.writeEvent(0x534e4554, "62187985"); // safety net logging
+                    Toast.makeText(v.getContext(),
+                            R.string.touch_filtered_warning,
+                            Toast.LENGTH_SHORT).show();
+                }
+                return true;
+            }
+            return false;
+        };
+        mAlert.getButton(BUTTON_POSITIVE).setOnTouchListener(filterTouchListener);
+
+    }
+
+    @Override
+    public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
+        super.onWindowAttributesChanged(params);
+    }
+
+    private class WifiChangeReceiver extends BroadcastReceiver {
+        private final Activity mActivity;
+        WifiChangeReceiver(Activity activity) {
+            mActivity = activity;
+        }
+
+        @Override
+        public void onReceive(Context content, Intent intent) {
+            String action = intent.getAction();
+            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
+                int state = intent.getIntExtra(
+                        WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
+                if (state == WifiManager.WIFI_STATE_DISABLED) {
+                    mActivity.finish();
+                }
+            } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+                NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
+                        WifiManager.EXTRA_NETWORK_INFO);
+                if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+                    if (!networkInfo.isConnected()) {
+                        mActivity.finish();
+                        return;
+                    }
+                    WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+                    if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+                        mActivity.finish();
+                        return;
+                    }
+                    String bssid = wifiInfo.getBSSID();
+                    if (bssid == null || bssid.isEmpty()) {
+                        mActivity.finish();
+                        return;
+                    }
+                    if (!bssid.equals(mBssid)) {
+                        mActivity.finish();
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        IntentFilter filter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        registerReceiver(mWifiChangeReceiver, filter);
+    }
+
+    @Override
+    protected void onStop() {
+        if (mWifiChangeReceiver != null) {
+            unregisterReceiver(mWifiChangeReceiver);
+        }
+        super.onStop();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        // In the case where user dismissed the dialog, we don't get an onClick event.
+        // In that case, tell adb to deny the network connection.
+        if (!mClicked) {
+            try {
+                IBinder b = ServiceManager.getService(ADB_SERVICE);
+                IAdbManager service = IAdbManager.Stub.asInterface(b);
+                service.denyWirelessDebugging();
+            } catch (Exception e) {
+                Log.e(TAG, "Unable to notify Adb service", e);
+            }
+        }
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        mClicked = true;
+        boolean allow = (which == AlertDialog.BUTTON_POSITIVE);
+        boolean alwaysAllow = allow && mAlwaysAllow.isChecked();
+        try {
+            IBinder b = ServiceManager.getService(ADB_SERVICE);
+            IAdbManager service = IAdbManager.Stub.asInterface(b);
+            if (allow) {
+                service.allowWirelessDebugging(alwaysAllow, mBssid);
+            } else {
+                service.denyWirelessDebugging();
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to notify Adb service", e);
+        }
+        finish();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java
new file mode 100644
index 0000000..0266a84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wifi;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import com.android.systemui.R;
+
+/**
+ * Alerts the user that wireless debugging cannot be enabled by a secondary user.
+ */
+public class WifiDebuggingSecondaryUserActivity extends AlertActivity
+        implements DialogInterface.OnClickListener {
+    private WifiChangeReceiver mWifiChangeReceiver;
+    private WifiManager mWifiManager;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+        mWifiChangeReceiver = new WifiChangeReceiver(this);
+
+        final AlertController.AlertParams ap = mAlertParams;
+        ap.mTitle = getString(R.string.wifi_debugging_secondary_user_title);
+        ap.mMessage = getString(R.string.wifi_debugging_secondary_user_message);
+        ap.mPositiveButtonText = getString(android.R.string.ok);
+        ap.mPositiveButtonListener = this;
+
+        setupAlert();
+    }
+
+    private class WifiChangeReceiver extends BroadcastReceiver {
+        private final Activity mActivity;
+        WifiChangeReceiver(Activity activity) {
+            mActivity = activity;
+        }
+
+        @Override
+        public void onReceive(Context content, Intent intent) {
+            String action = intent.getAction();
+            if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
+                int state = intent.getIntExtra(
+                        WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
+                if (state == WifiManager.WIFI_STATE_DISABLED) {
+                    mActivity.finish();
+                }
+            } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+                NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
+                        WifiManager.EXTRA_NETWORK_INFO);
+                if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+                    if (!networkInfo.isConnected()) {
+                        mActivity.finish();
+                        return;
+                    }
+                    WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+                    if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+                        mActivity.finish();
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+
+        IntentFilter filter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        registerReceiver(mWifiChangeReceiver, filter);
+    }
+
+    @Override
+    protected void onStop() {
+        if (mWifiChangeReceiver != null) {
+            unregisterReceiver(mWifiChangeReceiver);
+        }
+        super.onStop();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        finish();
+    }
+}
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 4efe934..0c37235 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -16,7 +16,9 @@
 
 java_defaults {
     name: "TetheringAndroidLibraryDefaults",
-    sdk_version: "system_current",
+    // TODO (b/146757305): change to module API once available
+    // TODO (b/148190005): change to module-libs-api-stubs-current once it is ready.
+    sdk_version: "core_platform",
     srcs: [
         "src/**/*.java",
         ":framework-tethering-shared-srcs",
@@ -33,8 +35,15 @@
         "net-utils-framework-common",
     ],
     libs: [
+        // Order matters: framework-tethering needs to be before the system stubs, otherwise
+        // hidden fields in the framework-tethering classes (which are also used to generate stubs)
+        // will not be found.
         "framework-tethering",
+        "android_system_stubs_current",
+        "framework-res",
         "unsupportedappusage",
+        "android_system_stubs_current",
+        "framework-res",
     ],
     plugins: ["java_api_finder"],
     manifest: "AndroidManifestBase.xml",
@@ -82,20 +91,23 @@
 // Common defaults for compiling the actual APK.
 java_defaults {
     name: "TetheringAppDefaults",
-    sdk_version: "system_current",
+    // TODO (b/146757305): change to module API once available
+    // TODO (b/148190005): change to module-libs-api-stubs-current once it is ready.
+    sdk_version: "core_platform",
     privileged: true,
-    // Build system doesn't track transitive dependeicies for jni_libs, list all the dependencies
-    // explicitly.
     jni_libs: [
-        "liblog",
-        "libnativehelper_compat_libc++",
         "libtetherutilsjni",
     ],
     resource_dirs: [
         "res",
     ],
     libs: [
+        // Order matters: framework-tethering needs to be before the system stubs, otherwise
+        // hidden fields in the framework-tethering classes (which are also used to generate stubs)
+        // will not be found.
         "framework-tethering",
+        "android_system_stubs_current",
+        "framework-res",
     ],
     jarjar_rules: "jarjar-rules.txt",
     optimize: {
diff --git a/packages/Tethering/TEST_MAPPING b/packages/Tethering/TEST_MAPPING
new file mode 100644
index 0000000..73254cd
--- /dev/null
+++ b/packages/Tethering/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "TetheringTests"
+    }
+  ]
+}
diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp
index 94ef11c..96a4d20 100644
--- a/packages/Tethering/apex/Android.bp
+++ b/packages/Tethering/apex/Android.bp
@@ -16,6 +16,7 @@
 
 apex {
     name: "com.android.tethering",
+    updatable: true,
     java_libs: ["framework-tethering"],
     apps: ["Tethering"],
     manifest: "manifest.json",
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index e0adb34d..5b73dd5 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -41,12 +41,11 @@
 
 java_library {
     name: "framework-tethering",
-    sdk_version: "system_current",
+    sdk_version: "module_current",
     srcs: [
         "src/android/net/TetheredClient.java",
         "src/android/net/TetheringManager.java",
         "src/android/net/TetheringConstants.java",
-        ":framework-tethering-annotations",
     ],
     static_libs: [
         "tethering-aidl-interfaces-java",
@@ -55,20 +54,36 @@
     installable: true,
 
     libs: [
-        "android_system_stubs_current",
+        "framework-annotations-lib",
     ],
 
     hostdex: true, // for hiddenapi check
-    visibility: [
-        "//frameworks/base/packages/Tethering:__subpackages__",
-        //TODO(b/147200698) remove below lines when the platform is built with stubs
-        "//frameworks/base",
-        "//frameworks/base/services",
-        "//frameworks/base/services/core",
-    ],
+    visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
     apex_available: ["com.android.tethering"],
 }
 
+droidstubs {
+    name: "framework-tethering-stubs-sources",
+    defaults: ["framework-module-stubs-defaults-module_libs_api"],
+    srcs: [
+        "src/android/net/TetheredClient.java",
+        "src/android/net/TetheringManager.java",
+        "src/android/net/TetheringConstants.java",
+    ],
+    libs: [
+        "tethering-aidl-interfaces-java",
+        "framework-all",
+    ],
+    sdk_version: "core_platform",
+}
+
+java_library {
+    name: "framework-tethering-stubs",
+    srcs: [":framework-tethering-stubs-sources"],
+    libs: ["framework-all"],
+    sdk_version: "core_platform",
+}
+
 filegroup {
     name: "framework-tethering-srcs",
     srcs: [
diff --git a/packages/Tethering/common/TetheringLib/jarjar-rules.txt b/packages/Tethering/common/TetheringLib/jarjar-rules.txt
index 1403bba..e459fad 100644
--- a/packages/Tethering/common/TetheringLib/jarjar-rules.txt
+++ b/packages/Tethering/common/TetheringLib/jarjar-rules.txt
@@ -1,2 +1 @@
-rule android.annotation.** com.android.networkstack.tethering.annotation.@1
-rule com.android.internal.annotations.** com.android.networkstack.tethering.annotation.@1
\ No newline at end of file
+# jarjar rules for the bootclasspath tethering framework library here
\ No newline at end of file
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
index 5febe73..8be7964 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
@@ -1,16 +1,16 @@
-/**
- * Copyright (c) 2019, The Android Open Source Project
+/*
+ * Copyright (C) 2020 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
+ *      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 perNmissions and
+ * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 package android.net;
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
index 28a810d..a554193 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.net.Network;
+import android.net.TetheredClient;
 import android.net.TetheringConfigurationParcel;
 import android.net.TetheringCallbackStartedParcel;
 import android.net.TetherStatesParcel;
@@ -33,4 +34,5 @@
     void onUpstreamChanged(in Network network);
     void onConfigurationChanged(in TetheringConfigurationParcel config);
     void onTetherStatesChanged(in TetherStatesParcel states);
+    void onTetherClientsChanged(in List<TetheredClient> clients);
 }
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index f5c9664..8b8b9e5 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -34,6 +36,7 @@
  * @hide
  */
 @SystemApi
+@SystemApi(client = MODULE_LIBRARIES)
 @TestApi
 public final class TetheredClient implements Parcelable {
     @NonNull
@@ -188,6 +191,15 @@
                 return new AddressInfo[size];
             }
         };
+
+        @NonNull
+        @Override
+        public String toString() {
+            return "AddressInfo {"
+                    + mAddress
+                    + (mHostname != null ? ", hostname " + mHostname : "")
+                    + "}";
+        }
     }
 
     @Override
@@ -209,4 +221,13 @@
             return new TetheredClient[size];
         }
     };
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "TetheredClient {hwAddr " + mMacAddress
+                + ", addresses " + mAddresses
+                + ", tetheringType " + mTetheringType
+                + "}";
+    }
 }
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl
index 14ee2d3e..c064aa4 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.net.Network;
+import android.net.TetheredClient;
 import android.net.TetheringConfigurationParcel;
 import android.net.TetherStatesParcel;
 
@@ -29,4 +30,5 @@
     Network upstreamNetwork;
     TetheringConfigurationParcel config;
     TetherStatesParcel states;
+    List<TetheredClient> tetheredClients;
 }
\ No newline at end of file
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index 00cf98e..a18f5da 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -16,6 +16,9 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.os.ResultReceiver;
 
 /**
@@ -28,39 +31,33 @@
  * symbols from framework-tethering even when they are in a non-hidden class.
  * @hide
  */
+@SystemApi(client = MODULE_LIBRARIES)
 public class TetheringConstants {
+    /** An explicit private class to avoid exposing constructor.*/
+    private TetheringConstants() { }
+
     /**
      * Extra used for communicating with the TetherService. Includes the type of tethering to
      * enable if any.
-     *
-     * {@hide}
      */
     public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
     /**
      * Extra used for communicating with the TetherService. Includes the type of tethering for
      * which to cancel provisioning.
-     *
-     * {@hide}
      */
     public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
     /**
      * Extra used for communicating with the TetherService. True to schedule a recheck of tether
      * provisioning.
-     *
-     * {@hide}
      */
     public static final String EXTRA_SET_ALARM = "extraSetAlarm";
     /**
      * Tells the TetherService to run a provision check now.
-     *
-     * {@hide}
      */
     public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
     /**
      * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
      * which will receive provisioning results. Can be left empty.
-     *
-     * {@hide}
      */
     public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
 }
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 53a358f..bfa962a 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -15,6 +15,8 @@
  */
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -50,6 +52,7 @@
  * @hide
  */
 @SystemApi
+@SystemApi(client = MODULE_LIBRARIES)
 @TestApi
 public class TetheringManager {
     private static final String TAG = TetheringManager.class.getSimpleName();
@@ -177,6 +180,7 @@
      *                          service is not connected.
      * {@hide}
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public TetheringManager(@NonNull final Context context,
             @NonNull Supplier<IBinder> connectorSupplier) {
         mContext = context;
@@ -371,6 +375,9 @@
             mTetherStatesParcel = states;
         }
 
+        @Override
+        public void onTetherClientsChanged(List<TetheredClient> clients) { }
+
         public void waitForStarted() {
             mWaitForCallback.block(DEFAULT_TIMEOUT_MS);
             throwIfPermissionFailure(mError);
@@ -395,6 +402,7 @@
      * {@hide}
      */
     @Deprecated
+    @SystemApi(client = MODULE_LIBRARIES)
     public int tether(@NonNull final String iface) {
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "tether caller:" + callerPkg);
@@ -418,6 +426,7 @@
      * {@hide}
      */
     @Deprecated
+    @SystemApi(client = MODULE_LIBRARIES)
     public int untether(@NonNull final String iface) {
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "untether caller:" + callerPkg);
@@ -444,6 +453,7 @@
      * {@hide}
      */
     @Deprecated
+    @SystemApi(client = MODULE_LIBRARIES)
     public int setUsbTethering(final boolean enable) {
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "setUsbTethering caller:" + callerPkg);
@@ -702,6 +712,7 @@
      * {@hide}
      */
     // TODO: improve the usage of ResultReceiver, b/145096122
+    @SystemApi(client = MODULE_LIBRARIES)
     public void requestLatestTetheringEntitlementResult(final int type,
             @NonNull final ResultReceiver receiver, final boolean showEntitlementUi) {
         final String callerPkg = mContext.getOpPackageName();
@@ -913,6 +924,7 @@
                         sendRegexpsChanged(parcel.config);
                         maybeSendTetherableIfacesChangedCallback(parcel.states);
                         maybeSendTetheredIfacesChangedCallback(parcel.states);
+                        callback.onClientsChanged(parcel.tetheredClients);
                     });
                 }
 
@@ -943,6 +955,11 @@
                         maybeSendTetheredIfacesChangedCallback(states);
                     });
                 }
+
+                @Override
+                public void onTetherClientsChanged(final List<TetheredClient> clients) {
+                    executor.execute(() -> callback.onClientsChanged(clients));
+                }
             };
             getConnector(c -> c.registerTetheringEventCallback(remoteCallback, callerPkg));
             mTetheringEventCallbacks.put(callback, remoteCallback);
@@ -982,6 +999,7 @@
      *               interface
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public int getLastTetherError(@NonNull final String iface) {
         mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR;
@@ -1004,6 +1022,7 @@
      *        what interfaces are considered tetherable usb interfaces.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public @NonNull String[] getTetherableUsbRegexs() {
         mCallback.waitForStarted();
         return mTetheringConfiguration.tetherableUsbRegexs;
@@ -1018,6 +1037,7 @@
      *        what interfaces are considered tetherable wifi interfaces.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public @NonNull String[] getTetherableWifiRegexs() {
         mCallback.waitForStarted();
         return mTetheringConfiguration.tetherableWifiRegexs;
@@ -1032,6 +1052,7 @@
      *        what interfaces are considered tetherable bluetooth interfaces.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public @NonNull String[] getTetherableBluetoothRegexs() {
         mCallback.waitForStarted();
         return mTetheringConfiguration.tetherableBluetoothRegexs;
@@ -1044,6 +1065,7 @@
      * @return an array of 0 or more Strings of tetherable interface names.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public @NonNull String[] getTetherableIfaces() {
         mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return new String[0];
@@ -1057,6 +1079,7 @@
      * @return an array of 0 or more String of currently tethered interface names.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public @NonNull String[] getTetheredIfaces() {
         mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return new String[0];
@@ -1076,6 +1099,7 @@
      *        which failed to tether.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public @NonNull String[] getTetheringErroredIfaces() {
         mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return new String[0];
@@ -1103,6 +1127,7 @@
      * @return a boolean - {@code true} indicating Tethering is supported.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public boolean isTetheringSupported() {
         final String callerPkg = mContext.getOpPackageName();
 
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 2653b6d..38f8609 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -19,6 +19,7 @@
 import static android.net.InetAddresses.parseNumericAddress;
 import static android.net.RouteInfo.RTN_UNICAST;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
+import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
 import static android.net.util.NetworkConstants.FF;
 import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
 import static android.net.util.NetworkConstants.asByte;
@@ -29,18 +30,24 @@
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
+import android.net.MacAddress;
 import android.net.RouteInfo;
+import android.net.TetheredClient;
 import android.net.TetheringManager;
+import android.net.dhcp.DhcpLeaseParcelable;
 import android.net.dhcp.DhcpServerCallbacks;
 import android.net.dhcp.DhcpServingParamsParcel;
 import android.net.dhcp.DhcpServingParamsParcelExt;
+import android.net.dhcp.IDhcpLeaseCallbacks;
 import android.net.dhcp.IDhcpServer;
+import android.net.ip.IpNeighborMonitor.NeighborEvent;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
 import android.net.shared.NetdUtils;
 import android.net.shared.RouteUtils;
 import android.net.util.InterfaceParams;
 import android.net.util.InterfaceSet;
 import android.net.util.SharedLog;
+import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
@@ -48,16 +55,24 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.util.MessageUtils;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 
+import java.io.IOException;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
+import java.net.NetworkInterface;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Objects;
 import java.util.Random;
 import java.util.Set;
@@ -130,10 +145,21 @@
          * @param newLp the new LinkProperties to report
          */
         public void updateLinkProperties(IpServer who, LinkProperties newLp) { }
+
+        /**
+         * Notify that the DHCP leases changed in one of the IpServers.
+         */
+        public void dhcpLeasesChanged() { }
     }
 
     /** Capture IpServer dependencies, for injection. */
     public abstract static class Dependencies {
+        /** Create an IpNeighborMonitor to be used by this IpServer */
+        public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log,
+                IpNeighborMonitor.NeighborEventConsumer consumer) {
+            return new IpNeighborMonitor(handler, log, consumer);
+        }
+
         /** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/
         public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
             return new RouterAdvertisementDaemon(ifParams);
@@ -144,6 +170,15 @@
             return InterfaceParams.getByName(ifName);
         }
 
+        /** Get |ifName|'s interface index. */
+        public int getIfindex(String ifName) {
+            try {
+                return NetworkInterface.getByName(ifName).getIndex();
+            } catch (IOException | NullPointerException e) {
+                Log.e(TAG, "Can't determine interface index for interface " + ifName);
+                return 0;
+            }
+        }
         /** Create a DhcpServer instance to be used by IpServer. */
         public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
                 DhcpServerCallbacks cb);
@@ -169,6 +204,8 @@
     public static final int CMD_TETHER_CONNECTION_CHANGED   = BASE_IPSERVER + 9;
     // new IPv6 tethering parameters need to be processed
     public static final int CMD_IPV6_TETHER_UPDATE          = BASE_IPSERVER + 10;
+    // new neighbor cache entry on our interface
+    public static final int CMD_NEIGHBOR_EVENT              = BASE_IPSERVER + 11;
 
     private final State mInitialState;
     private final State mLocalHotspotState;
@@ -205,6 +242,42 @@
     private IDhcpServer mDhcpServer;
     private RaParams mLastRaParams;
     private LinkAddress mIpv4Address;
+    @NonNull
+    private List<TetheredClient> mDhcpLeases = Collections.emptyList();
+
+    private int mLastIPv6UpstreamIfindex = 0;
+
+    private class MyNeighborEventConsumer implements IpNeighborMonitor.NeighborEventConsumer {
+        public void accept(NeighborEvent e) {
+            sendMessage(CMD_NEIGHBOR_EVENT, e);
+        }
+    }
+
+    static class Ipv6ForwardingRule {
+        public final int upstreamIfindex;
+        public final int downstreamIfindex;
+        public final Inet6Address address;
+        public final MacAddress srcMac;
+        public final MacAddress dstMac;
+
+        Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, Inet6Address address,
+                MacAddress srcMac, MacAddress dstMac) {
+            this.upstreamIfindex = upstreamIfindex;
+            this.downstreamIfindex = downstreamIfIndex;
+            this.address = address;
+            this.srcMac = srcMac;
+            this.dstMac = dstMac;
+        }
+
+        public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) {
+            return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
+                    dstMac);
+        }
+    }
+    private final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> mIpv6ForwardingRules =
+            new LinkedHashMap<>();
+
+    private final IpNeighborMonitor mIpNeighborMonitor;
 
     public IpServer(
             String ifaceName, Looper looper, int interfaceType, SharedLog log,
@@ -223,6 +296,12 @@
         mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
         mServingMode = STATE_AVAILABLE;
 
+        mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog,
+                new MyNeighborEventConsumer());
+        if (!mIpNeighborMonitor.start()) {
+            mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName);
+        }
+
         mInitialState = new InitialState();
         mLocalHotspotState = new LocalHotspotState();
         mTetheredState = new TetheredState();
@@ -262,6 +341,14 @@
         return new LinkProperties(mLinkProperties);
     }
 
+    /**
+     * Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper
+     * thread.
+     */
+    public List<TetheredClient> getAllLeases() {
+        return Collections.unmodifiableList(mDhcpLeases);
+    }
+
     /** Stop this IpServer. After this is called this IpServer should not be used any more. */
     public void stop() {
         sendMessage(CMD_INTERFACE_DOWN);
@@ -334,7 +421,7 @@
 
                 mDhcpServer = server;
                 try {
-                    mDhcpServer.start(new OnHandlerStatusCallback() {
+                    mDhcpServer.startWithCallbacks(new OnHandlerStatusCallback() {
                         @Override
                         public void callback(int startStatusCode) {
                             if (startStatusCode != STATUS_SUCCESS) {
@@ -342,7 +429,7 @@
                                 handleError();
                             }
                         }
-                    });
+                    }, new DhcpLeaseCallback());
                 } catch (RemoteException e) {
                     throw new IllegalStateException(e);
                 }
@@ -355,6 +442,48 @@
         }
     }
 
+    private class DhcpLeaseCallback extends IDhcpLeaseCallbacks.Stub {
+        @Override
+        public void onLeasesChanged(List<DhcpLeaseParcelable> leaseParcelables) {
+            final ArrayList<TetheredClient> leases = new ArrayList<>();
+            for (DhcpLeaseParcelable lease : leaseParcelables) {
+                final LinkAddress address = new LinkAddress(
+                        intToInet4AddressHTH(lease.netAddr), lease.prefixLength);
+
+                final MacAddress macAddress;
+                try {
+                    macAddress = MacAddress.fromBytes(lease.hwAddr);
+                } catch (IllegalArgumentException e) {
+                    Log.wtf(TAG, "Invalid address received from DhcpServer: "
+                            + Arrays.toString(lease.hwAddr));
+                    return;
+                }
+
+                final TetheredClient.AddressInfo addressInfo = new TetheredClient.AddressInfo(
+                        address, lease.hostname, lease.expTime);
+                leases.add(new TetheredClient(
+                        macAddress,
+                        Collections.singletonList(addressInfo),
+                        mInterfaceType));
+            }
+
+            getHandler().post(() -> {
+                mDhcpLeases = leases;
+                mCallback.dhcpLeasesChanged();
+            });
+        }
+
+        @Override
+        public int getInterfaceVersion() {
+            return this.VERSION;
+        }
+
+        @Override
+        public String getInterfaceHash() throws RemoteException {
+            return this.HASH;
+        }
+    }
+
     private boolean startDhcp(Inet4Address addr, int prefixLen) {
         if (mUsingLegacyDhcp) {
             return true;
@@ -388,6 +517,8 @@
                             mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
                             // Not much more we can do here
                         }
+                        mDhcpLeases.clear();
+                        getHandler().post(mCallback::dhcpLeasesChanged);
                     }
                 });
                 mDhcpServer = null;
@@ -538,13 +669,21 @@
         }
 
         RaParams params = null;
+        int upstreamIfindex = 0;
 
         if (v6only != null) {
+            final String upstreamIface = v6only.getInterfaceName();
+
             params = new RaParams();
-            params.mtu = v6only.getMtu();
+            // We advertise an mtu lower by 16, which is the closest multiple of 8 >= 14,
+            // the ethernet header size.  This makes kernel ebpf tethering offload happy.
+            // This hack should be reverted once we have the kernel fixed up.
+            // Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu)
+            // see RouterAdvertisementDaemon.java putMtu()
+            params.mtu = v6only.getMtu() - 16;
             params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
 
-            if (params.hasDefaultRoute) params.hopLimit = getHopLimit(v6only.getInterfaceName());
+            if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface);
 
             for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
                 if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
@@ -558,12 +697,18 @@
                     params.dnses.add(dnsServer);
                 }
             }
+
+            upstreamIfindex = mDeps.getIfindex(upstreamIface);
         }
+
         // If v6only is null, we pass in null to setRaParams(), which handles
         // deprecation of any existing RA data.
 
         setRaParams(params);
         mLastIPv6LinkProperties = v6only;
+
+        updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfindex, null);
+        mLastIPv6UpstreamIfindex = upstreamIfindex;
     }
 
     private void configureLocalIPv6Routes(
@@ -658,6 +803,87 @@
         }
     }
 
+    private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) {
+        try {
+            mNetd.tetherRuleAddDownstreamIpv6(mInterfaceParams.index, rule.upstreamIfindex,
+                    rule.address.getAddress(),  mInterfaceParams.macAddr.toByteArray(),
+                    rule.dstMac.toByteArray());
+            mIpv6ForwardingRules.put(rule.address, rule);
+        } catch (RemoteException | ServiceSpecificException e) {
+            mLog.e("Could not add IPv6 downstream rule: ", e);
+        }
+    }
+
+    private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) {
+        try {
+            mNetd.tetherRuleRemoveDownstreamIpv6(rule.upstreamIfindex, rule.address.getAddress());
+            if (removeFromMap) {
+                mIpv6ForwardingRules.remove(rule.address);
+            }
+        } catch (RemoteException | ServiceSpecificException e) {
+            mLog.e("Could not remove IPv6 downstream rule: ", e);
+        }
+    }
+
+    private void clearIpv6ForwardingRules() {
+        for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) {
+            removeIpv6ForwardingRule(rule, false /*removeFromMap*/);
+        }
+        mIpv6ForwardingRules.clear();
+    }
+
+    // Convenience method to replace a rule with the same rule on a new upstream interface.
+    // Allows replacing the rules in one iteration pass without ConcurrentModificationExceptions.
+    // Relies on the fact that rules are in a map indexed by IP address.
+    private void updateIpv6ForwardingRule(Ipv6ForwardingRule rule, int newIfindex) {
+        addIpv6ForwardingRule(rule.onNewUpstream(newIfindex));
+        removeIpv6ForwardingRule(rule, false /*removeFromMap*/);
+    }
+
+    // Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream
+    // changes or if a neighbor event is received.
+    private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex,
+            NeighborEvent e) {
+        // If we no longer have an upstream, clear forwarding rules and do nothing else.
+        if (upstreamIfindex == 0) {
+            clearIpv6ForwardingRules();
+            return;
+        }
+
+        // If the upstream interface has changed, remove all rules and re-add them with the new
+        // upstream interface.
+        if (prevUpstreamIfindex != upstreamIfindex) {
+            for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) {
+                updateIpv6ForwardingRule(rule, upstreamIfindex);
+            }
+        }
+
+        // If we're here to process a NeighborEvent, do so now.
+        // mInterfaceParams must be non-null or the event would not have arrived.
+        if (e == null) return;
+        if (!(e.ip instanceof Inet6Address) || e.ip.isMulticastAddress()
+                || e.ip.isLoopbackAddress() || e.ip.isLinkLocalAddress()) {
+            return;
+        }
+
+        Ipv6ForwardingRule rule = new Ipv6ForwardingRule(upstreamIfindex,
+                mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr,
+                e.macAddr);
+        if (e.isValid()) {
+            addIpv6ForwardingRule(rule);
+        } else {
+            removeIpv6ForwardingRule(rule, true /*removeFromMap*/);
+        }
+    }
+
+    private void handleNeighborEvent(NeighborEvent e) {
+        if (mInterfaceParams != null
+                && mInterfaceParams.index == e.ifindex
+                && mInterfaceParams.hasMacAddress) {
+            updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex, e);
+        }
+    }
+
     private byte getHopLimit(String upstreamIface) {
         try {
             int upstreamHopLimit = Integer.parseUnsignedInt(
@@ -883,6 +1109,7 @@
 
             for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);
             mUpstreamIfaceSet = null;
+            clearIpv6ForwardingRules();
         }
 
         private void cleanupUpstreamInterface(String upstreamIface) {
@@ -945,6 +1172,9 @@
                         }
                     }
                     break;
+                case CMD_NEIGHBOR_EVENT:
+                    handleNeighborEvent((NeighborEvent) message.obj);
+                    break;
                 default:
                     return false;
             }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java b/packages/Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java
new file mode 100644
index 0000000..cdd1a5d
--- /dev/null
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 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.connectivity.tethering;
+
+import static android.net.TetheringManager.TETHERING_WIFI;
+
+import android.net.MacAddress;
+import android.net.TetheredClient;
+import android.net.TetheredClient.AddressInfo;
+import android.net.ip.IpServer;
+import android.net.wifi.WifiClient;
+import android.os.SystemClock;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tracker for clients connected to downstreams.
+ *
+ * <p>This class is not thread safe, it is intended to be used only from the tethering handler
+ * thread.
+ */
+public class ConnectedClientsTracker {
+    private final Clock mClock;
+
+    @NonNull
+    private List<WifiClient> mLastWifiClients = Collections.emptyList();
+    @NonNull
+    private List<TetheredClient> mLastTetheredClients = Collections.emptyList();
+
+    @VisibleForTesting
+    static class Clock {
+        public long elapsedRealtime() {
+            return SystemClock.elapsedRealtime();
+        }
+    }
+
+    public ConnectedClientsTracker() {
+        this(new Clock());
+    }
+
+    @VisibleForTesting
+    ConnectedClientsTracker(Clock clock) {
+        mClock = clock;
+    }
+
+    /**
+     * Update the tracker with new connected clients.
+     *
+     * <p>The new list can be obtained through {@link #getLastTetheredClients()}.
+     * @param ipServers The IpServers used to assign addresses to clients.
+     * @param wifiClients The list of L2-connected WiFi clients. Null for no change since last
+     *                    update.
+     * @return True if the list of clients changed since the last calculation.
+     */
+    public boolean updateConnectedClients(
+            Iterable<IpServer> ipServers, @Nullable List<WifiClient> wifiClients) {
+        final long now = mClock.elapsedRealtime();
+
+        if (wifiClients != null) {
+            mLastWifiClients = wifiClients;
+        }
+        final Set<MacAddress> wifiClientMacs = getClientMacs(mLastWifiClients);
+
+        // Build the list of non-expired leases from all IpServers, grouped by mac address
+        final Map<MacAddress, TetheredClient> clientsMap = new HashMap<>();
+        for (IpServer server : ipServers) {
+            for (TetheredClient client : server.getAllLeases()) {
+                if (client.getTetheringType() == TETHERING_WIFI
+                        && !wifiClientMacs.contains(client.getMacAddress())) {
+                    // Skip leases of WiFi clients that are not (or no longer) L2-connected
+                    continue;
+                }
+                final TetheredClient prunedClient = pruneExpired(client, now);
+                if (prunedClient == null) continue; // All addresses expired
+
+                addLease(clientsMap, prunedClient);
+            }
+        }
+
+        // TODO: add IPv6 addresses from netlink
+
+        // Add connected WiFi clients that do not have any known address
+        for (MacAddress client : wifiClientMacs) {
+            if (clientsMap.containsKey(client)) continue;
+            clientsMap.put(client, new TetheredClient(
+                    client, Collections.emptyList() /* addresses */, TETHERING_WIFI));
+        }
+
+        final HashSet<TetheredClient> clients = new HashSet<>(clientsMap.values());
+        final boolean clientsChanged = clients.size() != mLastTetheredClients.size()
+                || !clients.containsAll(mLastTetheredClients);
+        mLastTetheredClients = Collections.unmodifiableList(new ArrayList<>(clients));
+        return clientsChanged;
+    }
+
+    private static void addLease(Map<MacAddress, TetheredClient> clientsMap, TetheredClient lease) {
+        final TetheredClient aggregateClient = clientsMap.getOrDefault(
+                lease.getMacAddress(), lease);
+        if (aggregateClient == lease) {
+            // This is the first lease with this mac address
+            clientsMap.put(lease.getMacAddress(), lease);
+            return;
+        }
+
+        // Only add the address info; this assumes that the tethering type is the same when the mac
+        // address is the same. If a client is connected through different tethering types with the
+        // same mac address, connected clients callbacks will report all of its addresses under only
+        // one of these tethering types. This keeps the API simple considering that such a scenario
+        // would really be a rare edge case.
+        clientsMap.put(lease.getMacAddress(), aggregateClient.addAddresses(lease));
+    }
+
+    /**
+     * Get the last list of tethered clients, as calculated in {@link #updateConnectedClients}.
+     *
+     * <p>The returned list is immutable.
+     */
+    @NonNull
+    public List<TetheredClient> getLastTetheredClients() {
+        return mLastTetheredClients;
+    }
+
+    private static boolean hasExpiredAddress(List<AddressInfo> addresses, long now) {
+        for (AddressInfo info : addresses) {
+            if (info.getExpirationTime() <= now) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Nullable
+    private static TetheredClient pruneExpired(TetheredClient client, long now) {
+        final List<AddressInfo> addresses = client.getAddresses();
+        if (addresses.size() == 0) return null;
+        if (!hasExpiredAddress(addresses, now)) return client;
+
+        final ArrayList<AddressInfo> newAddrs = new ArrayList<>(addresses.size() - 1);
+        for (AddressInfo info : addresses) {
+            if (info.getExpirationTime() > now) {
+                newAddrs.add(info);
+            }
+        }
+
+        if (newAddrs.size() == 0) {
+            return null;
+        }
+        return new TetheredClient(client.getMacAddress(), newAddrs, client.getTetheringType());
+    }
+
+    @NonNull
+    private static Set<MacAddress> getClientMacs(@NonNull List<WifiClient> clients) {
+        final Set<MacAddress> macs = new HashSet<>(clients.size());
+        for (WifiClient c : clients) {
+            macs.add(c.getMacAddress());
+        }
+        return macs;
+    }
+}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
index cc36f4a..a402ffa 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
@@ -288,10 +288,18 @@
 
         @Override
         public void setLimit(String iface, long quotaBytes) {
-            mLog.i("setLimit: " + iface + "," + quotaBytes);
             // Listen for all iface is necessary since upstream might be changed after limit
             // is set.
             mHandler.post(() -> {
+                final Long curIfaceQuota = mInterfaceQuotas.get(iface);
+
+                // If the quota is set to unlimited, the value set to HAL is Long.MAX_VALUE,
+                // which is ~8.4 x 10^6 TiB, no one can actually reach it. Thus, it is not
+                // useful to set it multiple times.
+                // Otherwise, the quota needs to be updated to tell HAL to re-count from now even
+                // if the quota is the same as the existing one.
+                if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return;
+
                 if (quotaBytes == QUOTA_UNLIMITED) {
                     mInterfaceQuotas.remove(iface);
                 } else {
@@ -323,7 +331,6 @@
 
         @Override
         public void requestStatsUpdate(int token) {
-            mLog.i("requestStatsUpdate: " + token);
             // Do not attempt to update stats by querying the offload HAL
             // synchronously from a different thread than the Handler thread. http://b/64771555.
             mHandler.post(() -> {
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 1edbbf8..0958e8a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.NETWORK_STACK;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.hardware.usb.UsbManager.USB_CONFIGURED;
 import static android.hardware.usb.UsbManager.USB_CONNECTED;
@@ -24,6 +26,7 @@
 import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
 import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER;
@@ -79,6 +82,7 @@
 import android.net.Network;
 import android.net.NetworkInfo;
 import android.net.TetherStatesParcel;
+import android.net.TetheredClient;
 import android.net.TetheringCallbackStartedParcel;
 import android.net.TetheringConfigurationParcel;
 import android.net.TetheringRequestParcel;
@@ -89,6 +93,7 @@
 import android.net.util.PrefixUtils;
 import android.net.util.SharedLog;
 import android.net.util.VersionedBroadcastListener;
+import android.net.wifi.WifiClient;
 import android.net.wifi.WifiManager;
 import android.net.wifi.p2p.WifiP2pGroup;
 import android.net.wifi.p2p.WifiP2pInfo;
@@ -128,8 +133,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
@@ -176,6 +183,17 @@
         }
     }
 
+    /**
+     * Cookie added when registering {@link android.net.TetheringManager.TetheringEventCallback}.
+     */
+    private static class CallbackCookie {
+        public final boolean hasListClientsPermission;
+
+        private CallbackCookie(boolean hasListClientsPermission) {
+            this.hasListClientsPermission = hasListClientsPermission;
+        }
+    }
+
     private final SharedLog mLog = new SharedLog(TAG);
     private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks =
             new RemoteCallbackList<>();
@@ -191,7 +209,8 @@
     private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
     // TODO: Figure out how to merge this and other downstream-tracking objects
     // into a single coherent structure.
-    private final HashSet<IpServer> mForwardedDownstreams;
+    // Use LinkedHashSet for predictable ordering order for ConnectedClientsTracker.
+    private final LinkedHashSet<IpServer> mForwardedDownstreams;
     private final VersionedBroadcastListener mCarrierConfigChange;
     private final TetheringDependencies mDeps;
     private final EntitlementManager mEntitlementMgr;
@@ -200,6 +219,8 @@
     private final NetdCallback mNetdCallback;
     private final UserRestrictionActionListener mTetheringRestriction;
     private final ActiveDataSubIdListener mActiveDataSubIdListener;
+    private final ConnectedClientsTracker mConnectedClientsTracker;
+    private final TetheringThreadExecutor mExecutor;
     private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
     // All the usage of mTetheringEventCallback should run in the same thread.
     private ITetheringEventCallback mTetheringEventCallback = null;
@@ -234,6 +255,7 @@
         mPublicSync = new Object();
 
         mTetherStates = new ArrayMap<>();
+        mConnectedClientsTracker = new ConnectedClientsTracker();
 
         mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
         mTetherMasterSM.start();
@@ -246,7 +268,7 @@
                 statsManager, mLog);
         mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
                 TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
-        mForwardedDownstreams = new HashSet<>();
+        mForwardedDownstreams = new LinkedHashSet<>();
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
@@ -275,8 +297,8 @@
         final UserManager userManager = (UserManager) mContext.getSystemService(
                 Context.USER_SERVICE);
         mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
-        final TetheringThreadExecutor executor = new TetheringThreadExecutor(mHandler);
-        mActiveDataSubIdListener = new ActiveDataSubIdListener(executor);
+        mExecutor = new TetheringThreadExecutor(mHandler);
+        mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
 
         // Load tethering configuration.
         updateConfiguration();
@@ -291,6 +313,11 @@
 
         startStateMachineUpdaters(mHandler);
         startTrackDefaultNetwork();
+
+        final WifiManager wifiManager = getWifiManager();
+        if (wifiManager != null) {
+            wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
+        }
     }
 
     private void startStateMachineUpdaters(Handler handler) {
@@ -385,6 +412,24 @@
         }
     }
 
+    private class TetheringSoftApCallback implements WifiManager.SoftApCallback {
+        // TODO: Remove onStateChanged override when this method has default on
+        // WifiManager#SoftApCallback interface.
+        // Wifi listener for state change of the soft AP
+        @Override
+        public void onStateChanged(final int state, final int failureReason) {
+            // Nothing
+        }
+
+        // Called by wifi when the number of soft AP clients changed.
+        @Override
+        public void onConnectedClientsChanged(final List<WifiClient> clients) {
+            if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, clients)) {
+                reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
+            }
+        }
+    }
+
     void interfaceStatusChanged(String iface, boolean up) {
         // Never called directly: only called from interfaceLinkStateChanged.
         // See NetlinkHandler.cpp: notifyInterfaceChanged.
@@ -560,14 +605,17 @@
                 Context.ETHERNET_SERVICE);
         synchronized (mPublicSync) {
             if (enable) {
+                if (mEthernetCallback != null) return TETHER_ERROR_NO_ERROR;
+
                 mEthernetCallback = new EthernetCallback();
-                mEthernetIfaceRequest = em.requestTetheredInterface(mEthernetCallback);
+                mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback);
             } else {
-                if (mConfiguredEthernetIface != null) {
-                    stopEthernetTetheringLocked();
+                stopEthernetTetheringLocked();
+                if (mEthernetCallback != null) {
                     mEthernetIfaceRequest.release();
+                    mEthernetCallback = null;
+                    mEthernetIfaceRequest = null;
                 }
-                mEthernetCallback = null;
             }
         }
         return TETHER_ERROR_NO_ERROR;
@@ -1938,14 +1986,21 @@
 
     /** Register tethering event callback */
     void registerTetheringEventCallback(ITetheringEventCallback callback) {
+        final boolean hasListPermission =
+                hasCallingPermission(NETWORK_SETTINGS)
+                        || hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)
+                        || hasCallingPermission(NETWORK_STACK);
         mHandler.post(() -> {
-            mTetheringEventCallbacks.register(callback);
+            mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission));
             final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel();
             parcel.tetheringSupported = mDeps.isTetheringSupported();
             parcel.upstreamNetwork = mTetherUpstream;
             parcel.config = mConfig.toStableParcelable();
             parcel.states =
                     mTetherStatesParcel != null ? mTetherStatesParcel : emptyTetherStatesParcel();
+            parcel.tetheredClients = hasListPermission
+                    ? mConnectedClientsTracker.getLastTetheredClients()
+                    : Collections.emptyList();
             try {
                 callback.onCallbackStarted(parcel);
             } catch (RemoteException e) {
@@ -1965,6 +2020,10 @@
         return parcel;
     }
 
+    private boolean hasCallingPermission(@NonNull String permission) {
+        return mContext.checkCallingPermission(permission) == PERMISSION_GRANTED;
+    }
+
     /** Unregister tethering event callback */
     void unregisterTetheringEventCallback(ITetheringEventCallback callback) {
         mHandler.post(() -> {
@@ -2018,6 +2077,24 @@
         }
     }
 
+    private void reportTetherClientsChanged(List<TetheredClient> clients) {
+        final int length = mTetheringEventCallbacks.beginBroadcast();
+        try {
+            for (int i = 0; i < length; i++) {
+                try {
+                    final CallbackCookie cookie =
+                            (CallbackCookie) mTetheringEventCallbacks.getBroadcastCookie(i);
+                    if (!cookie.hasListClientsPermission) continue;
+                    mTetheringEventCallbacks.getBroadcastItem(i).onTetherClientsChanged(clients);
+                } catch (RemoteException e) {
+                    // Not really very much to do here.
+                }
+            }
+        } finally {
+            mTetheringEventCallbacks.finishBroadcast();
+        }
+    }
+
     void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) {
         // Binder.java closes the resource for us.
         @SuppressWarnings("resource")
@@ -2109,6 +2186,14 @@
             public void updateLinkProperties(IpServer who, LinkProperties newLp) {
                 notifyLinkPropertiesChanged(who, newLp);
             }
+
+            @Override
+            public void dhcpLeasesChanged() {
+                if (mConnectedClientsTracker.updateConnectedClients(
+                        mForwardedDownstreams, null /* wifiClients */)) {
+                    reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
+                }
+            }
         };
     }
 
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 13174c5..ddc095f 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -34,7 +34,13 @@
         "TetheringApiCurrentLib",
         "testables",
     ],
+    // TODO(b/147200698) change sdk_version to module-current and
+    // remove framework-minus-apex, ext, and framework-res
+    sdk_version: "core_platform",
     libs: [
+        "framework-minus-apex",
+        "ext",
+        "framework-res",
         "android.test.runner",
         "android.test.base",
         "android.test.mock",
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index f29ad78..948266d 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -29,6 +29,11 @@
 import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
 import static android.net.ip.IpServer.STATE_TETHERED;
 import static android.net.ip.IpServer.STATE_UNAVAILABLE;
+import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
+import static android.net.netlink.NetlinkConstants.RTM_NEWNEIGH;
+import static android.net.netlink.StructNdMsg.NUD_FAILED;
+import static android.net.netlink.StructNdMsg.NUD_REACHABLE;
+import static android.net.netlink.StructNdMsg.NUD_STALE;
 import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
 
 import static org.junit.Assert.assertEquals;
@@ -38,9 +43,11 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
@@ -52,6 +59,7 @@
 import static org.mockito.Mockito.when;
 
 import android.net.INetd;
+import android.net.InetAddresses;
 import android.net.InterfaceConfigurationParcel;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
@@ -61,6 +69,8 @@
 import android.net.dhcp.DhcpServingParamsParcel;
 import android.net.dhcp.IDhcpServer;
 import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.ip.IpNeighborMonitor.NeighborEvent;
+import android.net.ip.IpNeighborMonitor.NeighborEventConsumer;
 import android.net.util.InterfaceParams;
 import android.net.util.InterfaceSet;
 import android.net.util.SharedLog;
@@ -81,6 +91,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.net.Inet4Address;
+import java.net.InetAddress;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -88,6 +99,8 @@
     private static final String IFACE_NAME = "testnet1";
     private static final String UPSTREAM_IFACE = "upstream0";
     private static final String UPSTREAM_IFACE2 = "upstream1";
+    private static final int UPSTREAM_IFINDEX = 101;
+    private static final int UPSTREAM_IFINDEX2 = 102;
     private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
     private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
     private static final int DHCP_LEASE_TIME_SECS = 3600;
@@ -102,6 +115,7 @@
     @Mock private SharedLog mSharedLog;
     @Mock private IDhcpServer mDhcpServer;
     @Mock private RouterAdvertisementDaemon mRaDaemon;
+    @Mock private IpNeighborMonitor mIpNeighborMonitor;
     @Mock private IpServer.Dependencies mDependencies;
 
     @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
@@ -111,6 +125,7 @@
             ArgumentCaptor.forClass(LinkProperties.class);
     private IpServer mIpServer;
     private InterfaceConfigurationParcel mInterfaceConfiguration;
+    private NeighborEventConsumer mNeighborEventConsumer;
 
     private void initStateMachine(int interfaceType) throws Exception {
         initStateMachine(interfaceType, false /* usingLegacyDhcp */);
@@ -130,16 +145,28 @@
         }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
         when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
         when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
+
+        when(mDependencies.getIfindex(eq(UPSTREAM_IFACE))).thenReturn(UPSTREAM_IFINDEX);
+        when(mDependencies.getIfindex(eq(UPSTREAM_IFACE2))).thenReturn(UPSTREAM_IFINDEX2);
+
         mInterfaceConfiguration = new InterfaceConfigurationParcel();
         mInterfaceConfiguration.flags = new String[0];
         if (interfaceType == TETHERING_BLUETOOTH) {
             mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR;
             mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH;
         }
+
+        ArgumentCaptor<NeighborEventConsumer> neighborCaptor =
+                ArgumentCaptor.forClass(NeighborEventConsumer.class);
+        doReturn(mIpNeighborMonitor).when(mDependencies).getIpNeighborMonitor(any(), any(),
+                neighborCaptor.capture());
+
         mIpServer = new IpServer(
                 IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
                 mCallback, usingLegacyDhcp, mDependencies);
         mIpServer.start();
+        mNeighborEventConsumer = neighborCaptor.getValue();
+
         // Starting the state machine always puts us in a consistent state and notifies
         // the rest of the world that we've changed from an unknown to available state.
         mLooper.dispatchAll();
@@ -158,7 +185,9 @@
         initStateMachine(interfaceType, usingLegacyDhcp);
         dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
         if (upstreamIface != null) {
-            dispatchTetherConnectionChanged(upstreamIface);
+            LinkProperties lp = new LinkProperties();
+            lp.setInterfaceName(upstreamIface);
+            dispatchTetherConnectionChanged(upstreamIface, lp);
         }
         reset(mNetd, mCallback);
     }
@@ -170,6 +199,8 @@
 
     @Test
     public void startsOutAvailable() {
+        when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
+                .thenReturn(mIpNeighborMonitor);
         mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
                 mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies);
         mIpServer.start();
@@ -467,9 +498,139 @@
         verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
     }
 
+    private InetAddress addr(String addr) throws Exception {
+        return InetAddresses.parseNumericAddress(addr);
+    }
+
+    private void recvNewNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
+        mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_NEWNEIGH, ifindex, addr,
+                nudState, mac));
+        mLooper.dispatchAll();
+    }
+
+    private void recvDelNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
+        mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_DELNEIGH, ifindex, addr,
+                nudState, mac));
+        mLooper.dispatchAll();
+    }
+
+    @Test
+    public void addRemoveipv6ForwardingRules() throws Exception {
+        initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */);
+
+        final int myIfindex = TEST_IFACE_PARAMS.index;
+        final int notMyIfindex = myIfindex - 1;
+
+        final MacAddress myMac = TEST_IFACE_PARAMS.macAddr;
+        final InetAddress neighA = InetAddresses.parseNumericAddress("2001:db8::1");
+        final InetAddress neighB = InetAddresses.parseNumericAddress("2001:db8::2");
+        final InetAddress neighLL = InetAddresses.parseNumericAddress("fe80::1");
+        final InetAddress neighMC = InetAddresses.parseNumericAddress("ff02::1234");
+        final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
+        final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b");
+
+        reset(mNetd);
+
+        // Events on other interfaces are ignored.
+        recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA);
+        verifyNoMoreInteractions(mNetd);
+
+        // Events on this interface are received and sent to netd.
+        recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+        verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+                eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
+        reset(mNetd);
+
+        recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+        verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+                eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+        reset(mNetd);
+
+        // Link-local and multicast neighbors are ignored.
+        recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, macA);
+        verifyNoMoreInteractions(mNetd);
+        recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, macA);
+        verifyNoMoreInteractions(mNetd);
+
+        // A neighbor that is no longer valid causes the rule to be removed.
+        recvNewNeigh(myIfindex, neighA, NUD_FAILED, macA);
+        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()));
+        reset(mNetd);
+
+        // A neighbor that is deleted causes the rule to be removed.
+        recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
+        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+        reset(mNetd);
+
+        // Upstream changes result in deleting and re-adding the rules.
+        recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+        recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+        reset(mNetd);
+
+        InOrder inOrder = inOrder(mNetd);
+        LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName(UPSTREAM_IFACE2);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp);
+        inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2),
+                eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
+        inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
+                    eq(neighA.getAddress()));
+        inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2),
+                eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+        inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
+                eq(neighB.getAddress()));
+        reset(mNetd);
+
+        // When the upstream is lost, rules are removed.
+        dispatchTetherConnectionChanged(null, null);
+        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2),
+                eq(neighA.getAddress()));
+        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2),
+                eq(neighB.getAddress()));
+        reset(mNetd);
+
+        // If the upstream is IPv4-only, no rules are added.
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE);
+        reset(mNetd);
+        recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+        verifyNoMoreInteractions(mNetd);
+
+        // Rules can be added again once upstream IPv6 connectivity is available.
+        lp.setInterfaceName(UPSTREAM_IFACE);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
+        recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+        verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+                eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+        verify(mNetd, never()).tetherRuleAddDownstreamIpv6(anyInt(), anyInt(),
+                eq(neighA.getAddress()), any(), any());
+
+        // If upstream IPv6 connectivity is lost, rules are removed.
+        reset(mNetd);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, null);
+        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+
+        // When the interface goes down, rules are removed.
+        lp.setInterfaceName(UPSTREAM_IFACE);
+        dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
+        recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+        recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+        verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+                eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
+        verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+                eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+        reset(mNetd);
+
+        mIpServer.stop();
+        mLooper.dispatchAll();
+        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()));
+        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+        reset(mNetd);
+    }
+
     private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
         verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
-        verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).start(any());
+        verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
+                any(), any());
         final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue();
         // Last address byte is random
         assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr)));
@@ -507,10 +668,17 @@
      *
      * @see #dispatchCommand(int)
      * @param upstreamIface String name of upstream interface (or null)
+     * @param v6lp IPv6 LinkProperties of the upstream interface, or null for an IPv4-only upstream.
      */
+    private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp) {
+        dispatchTetherConnectionChanged(upstreamIface);
+        mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, v6lp);
+        mLooper.dispatchAll();
+    }
+
     private void dispatchTetherConnectionChanged(String upstreamIface) {
-        mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
-                new InterfaceSet(upstreamIface));
+        final InterfaceSet ifs = (upstreamIface != null) ? new InterfaceSet(upstreamIface) : null;
+        mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifs);
         mLooper.dispatchAll();
     }
 
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
new file mode 100644
index 0000000..56f3e21
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 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.connectivity.tethering
+
+import android.net.LinkAddress
+import android.net.MacAddress
+import android.net.TetheredClient
+import android.net.TetheredClient.AddressInfo
+import android.net.TetheringManager.TETHERING_USB
+import android.net.TetheringManager.TETHERING_WIFI
+import android.net.ip.IpServer
+import android.net.wifi.WifiClient
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class ConnectedClientsTrackerTest {
+
+    private val server1 = mock(IpServer::class.java)
+    private val server2 = mock(IpServer::class.java)
+    private val servers = listOf(server1, server2)
+
+    private val clock = TestClock(1324L)
+
+    private val client1Addr = MacAddress.fromString("01:23:45:67:89:0A")
+    private val client1 = TetheredClient(client1Addr, listOf(
+            AddressInfo(LinkAddress("192.168.43.44/32"), null /* hostname */, clock.time + 20)),
+            TETHERING_WIFI)
+    private val wifiClient1 = makeWifiClient(client1Addr)
+    private val client2Addr = MacAddress.fromString("02:34:56:78:90:AB")
+    private val client2Exp30AddrInfo = AddressInfo(
+            LinkAddress("192.168.43.45/32"), "my_hostname", clock.time + 30)
+    private val client2 = TetheredClient(client2Addr, listOf(
+            client2Exp30AddrInfo,
+            AddressInfo(LinkAddress("2001:db8:12::34/72"), "other_hostname", clock.time + 10)),
+            TETHERING_WIFI)
+    private val wifiClient2 = makeWifiClient(client2Addr)
+    private val client3Addr = MacAddress.fromString("03:45:67:89:0A:BC")
+    private val client3 = TetheredClient(client3Addr,
+            listOf(AddressInfo(LinkAddress("2001:db8:34::34/72"), "other_other_hostname",
+                    clock.time + 10)),
+            TETHERING_USB)
+
+    @Test
+    fun testUpdateConnectedClients() {
+        doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
+        doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases
+
+        val tracker = ConnectedClientsTracker(clock)
+        assertFalse(tracker.updateConnectedClients(servers, null))
+
+        // Obtain a lease for client 1
+        doReturn(listOf(client1)).`when`(server1).allLeases
+        assertSameClients(listOf(client1), assertNewClients(tracker, servers, listOf(wifiClient1)))
+
+        // Client 2 L2-connected, no lease yet
+        val client2WithoutAddr = TetheredClient(client2Addr, emptyList(), TETHERING_WIFI)
+        assertSameClients(listOf(client1, client2WithoutAddr),
+                assertNewClients(tracker, servers, listOf(wifiClient1, wifiClient2)))
+
+        // Client 2 lease obtained
+        doReturn(listOf(client1, client2)).`when`(server1).allLeases
+        assertSameClients(listOf(client1, client2), assertNewClients(tracker, servers, null))
+
+        // Client 3 lease obtained
+        doReturn(listOf(client3)).`when`(server2).allLeases
+        assertSameClients(listOf(client1, client2, client3),
+                assertNewClients(tracker, servers, null))
+
+        // Client 2 L2-disconnected
+        assertSameClients(listOf(client1, client3),
+                assertNewClients(tracker, servers, listOf(wifiClient1)))
+
+        // Client 1 L2-disconnected
+        assertSameClients(listOf(client3), assertNewClients(tracker, servers, emptyList()))
+
+        // Client 1 comes back
+        assertSameClients(listOf(client1, client3),
+                assertNewClients(tracker, servers, listOf(wifiClient1)))
+
+        // Leases lost, client 1 still L2-connected
+        doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
+        doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases
+        assertSameClients(listOf(TetheredClient(client1Addr, emptyList(), TETHERING_WIFI)),
+                assertNewClients(tracker, servers, null))
+    }
+
+    @Test
+    fun testUpdateConnectedClients_LeaseExpiration() {
+        val tracker = ConnectedClientsTracker(clock)
+        doReturn(listOf(client1, client2)).`when`(server1).allLeases
+        doReturn(listOf(client3)).`when`(server2).allLeases
+        assertSameClients(listOf(client1, client2, client3), assertNewClients(
+                tracker, servers, listOf(wifiClient1, wifiClient2)))
+
+        clock.time += 20
+        // Client 3 has no remaining lease: removed
+        val expectedClients = listOf(
+                // Client 1 has no remaining lease but is L2-connected
+                TetheredClient(client1Addr, emptyList(), TETHERING_WIFI),
+                // Client 2 has some expired leases
+                TetheredClient(
+                        client2Addr,
+                        // Only the "t + 30" address is left, the "t + 10" address expired
+                        listOf(client2Exp30AddrInfo),
+                        TETHERING_WIFI))
+        assertSameClients(expectedClients, assertNewClients(tracker, servers, null))
+    }
+
+    private fun assertNewClients(
+        tracker: ConnectedClientsTracker,
+        ipServers: Iterable<IpServer>,
+        wifiClients: List<WifiClient>?
+    ): List<TetheredClient> {
+        assertTrue(tracker.updateConnectedClients(ipServers, wifiClients))
+        return tracker.lastTetheredClients
+    }
+
+    private fun assertSameClients(expected: List<TetheredClient>, actual: List<TetheredClient>) {
+        val expectedSet = HashSet(expected)
+        assertEquals(expected.size, expectedSet.size)
+        assertEquals(expectedSet, HashSet(actual))
+    }
+
+    private fun makeWifiClient(macAddr: MacAddress): WifiClient {
+        // Use a mock WifiClient as the constructor is not part of the WiFi module exported API.
+        return mock(WifiClient::class.java).apply { doReturn(macAddr).`when`(this).macAddress }
+    }
+
+    private class TestClock(var time: Long) : ConnectedClientsTracker.Clock() {
+        override fun elapsedRealtime(): Long {
+            return time
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index a917849..4581b56 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -28,6 +28,7 @@
 import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
 import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER;
 import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
 import static android.net.TetheringManager.TETHERING_NCM;
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
@@ -75,6 +76,8 @@
 import android.content.res.Resources;
 import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
+import android.net.EthernetManager;
+import android.net.EthernetManager.TetheredInterfaceRequest;
 import android.net.INetd;
 import android.net.ITetheringEventCallback;
 import android.net.InetAddresses;
@@ -88,12 +91,14 @@
 import android.net.NetworkRequest;
 import android.net.RouteInfo;
 import android.net.TetherStatesParcel;
+import android.net.TetheredClient;
 import android.net.TetheringCallbackStartedParcel;
 import android.net.TetheringConfigurationParcel;
 import android.net.TetheringRequestParcel;
 import android.net.dhcp.DhcpServerCallbacks;
 import android.net.dhcp.DhcpServingParamsParcel;
 import android.net.dhcp.IDhcpServer;
+import android.net.ip.IpNeighborMonitor;
 import android.net.ip.IpServer;
 import android.net.ip.RouterAdvertisementDaemon;
 import android.net.util.InterfaceParams;
@@ -142,6 +147,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
 import java.util.Vector;
 
 @RunWith(AndroidJUnit4.class)
@@ -171,11 +177,13 @@
     @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
     @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
     @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
+    @Mock private IpNeighborMonitor mIpNeighborMonitor;
     @Mock private IDhcpServer mDhcpServer;
     @Mock private INetd mNetd;
     @Mock private UserManager mUserManager;
     @Mock private NetworkRequest mNetworkRequest;
     @Mock private ConnectivityManager mCm;
+    @Mock private EthernetManager mEm;
 
     private final MockIpServerDependencies mIpServerDependencies =
             spy(new MockIpServerDependencies());
@@ -228,6 +236,7 @@
             if (Context.USER_SERVICE.equals(name)) return mUserManager;
             if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
             if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
+            if (Context.ETHERNET_SERVICE.equals(name)) return mEm;
             return super.getSystemService(name);
         }
 
@@ -276,6 +285,11 @@
                 }
             }).run();
         }
+
+        public IpNeighborMonitor getIpNeighborMonitor(Handler h, SharedLog l,
+                IpNeighborMonitor.NeighborEventConsumer c) {
+            return mIpNeighborMonitor;
+        }
     }
 
     private class MockTetheringConfiguration extends TetheringConfiguration {
@@ -470,6 +484,7 @@
                 ArgumentCaptor.forClass(PhoneStateListener.class);
         verify(mTelephonyManager).listen(phoneListenerCaptor.capture(),
                 eq(PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE));
+        verify(mWifiManager).registerSoftApCallback(any(), any());
         mPhoneStateListener = phoneListenerCaptor.getValue();
     }
 
@@ -728,7 +743,8 @@
 
         sendIPv6TetherUpdates(upstreamState);
         verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
-        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+                any(), any());
     }
 
     @Test
@@ -764,7 +780,8 @@
         verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mRouterAdvertisementDaemon, times(1)).start();
-        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+                any(), any());
 
         sendIPv6TetherUpdates(upstreamState);
         verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
@@ -778,7 +795,8 @@
 
         verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
         verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
-        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+                any(), any());
         verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
         verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
 
@@ -794,7 +812,8 @@
         runUsbTethering(upstreamState);
 
         verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
-        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+                any(), any());
         verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
 
         // Then 464xlat comes up
@@ -817,7 +836,8 @@
         verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
         // DHCP not restarted on downstream (still times(1))
-        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+                any(), any());
     }
 
     @Test
@@ -847,7 +867,8 @@
     public void workingNcmTethering() throws Exception {
         runNcmTethering();
 
-        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
+        verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+                any(), any());
     }
 
     @Test
@@ -1171,6 +1192,11 @@
         }
 
         @Override
+        public void onTetherClientsChanged(List<TetheredClient> clients) {
+            // TODO: check this
+        }
+
+        @Override
         public void onCallbackStarted(TetheringCallbackStartedParcel parcel) {
             mActualUpstreams.add(parcel.upstreamNetwork);
             mTetheringConfigs.add(parcel.config);
@@ -1295,6 +1321,24 @@
         assertEquals(fakeSubId, newConfig.activeDataSubId);
     }
 
+    @Test
+    public void testNoDuplicatedEthernetRequest() throws Exception {
+        final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class);
+        when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
+        mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null);
+        mLooper.dispatchAll();
+        verify(mEm, times(1)).requestTetheredInterface(any(), any());
+        mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null);
+        mLooper.dispatchAll();
+        verifyNoMoreInteractions(mEm);
+        mTethering.stopTethering(TETHERING_ETHERNET);
+        mLooper.dispatchAll();
+        verify(mockRequest, times(1)).release();
+        mTethering.stopTethering(TETHERING_ETHERNET);
+        mLooper.dispatchAll();
+        verifyNoMoreInteractions(mEm);
+    }
+
     private void workingWifiP2pGroupOwner(
             boolean emulateInterfaceStatusChanged) throws Exception {
         if (emulateInterfaceStatusChanged) {
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index ef071a4..2c7ea310 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -230,6 +230,10 @@
     // Package: android
     NOTE_TEST_HARNESS_MODE_ENABLED = 54;
 
+    // Display the Android Debug Protocol status
+    // Package: android
+    NOTE_ADB_WIFI_ACTIVE = 62;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/Android.bp b/services/Android.bp
index 073fccc..5019bb1 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -72,7 +72,7 @@
 
     libs: [
         "android.hidl.manager-V1.0-java",
-        "framework-tethering"
+        "framework-tethering-stubs",
     ],
 
     plugins: [
@@ -110,22 +110,30 @@
     name: "services-stubs.sources",
     srcs: [":services-sources"],
     installable: false,
-    // TODO: remove the --hide options below
     args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)" +
         " --hide-annotation android.annotation.Hide" +
+        " --hide InternalClasses" + // com.android.* classes are okay in this interface
+        // TODO: remove the --hide options below
         " --hide-package com.google.android.startop.iorap" +
         " --hide ReferencesHidden" +
         " --hide DeprecationMismatch" +
         " --hide HiddenTypedefConstant",
-    libs: [
-        "framework-all",
-    ],
     visibility: ["//visibility:private"],
     check_api: {
         current: {
             api_file: "api/current.txt",
             removed_api_file: "api/removed.txt",
         },
+        last_released: {
+            api_file: ":last-released-system-server-api",
+            removed_api_file: "api/removed.txt",
+            baseline_file: ":system-server-api-incompatibilities-with-last-released"
+        },
+        api_lint: {
+            enabled: true,
+            new_since: ":last-released-system-server-api",
+            baseline_file: "api/lint-baseline.txt",
+        },
     },
 }
 
diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS
index 265674a..c6f42f7 100644
--- a/services/accessibility/OWNERS
+++ b/services/accessibility/OWNERS
@@ -1,3 +1,4 @@
 svetoslavganov@google.com
 pweaver@google.com
 rhedjao@google.com
+qasid@google.com
diff --git a/services/api/lint-baseline.txt b/services/api/lint-baseline.txt
new file mode 100644
index 0000000..9a97707
--- /dev/null
+++ b/services/api/lint-baseline.txt
@@ -0,0 +1 @@
+// Baseline format: 1.0
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 649b5ea..123c5d5 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -29,7 +29,8 @@
         "android.hardware.tv.cec-V1.0-java",
         "android.hardware.vibrator-java",
         "app-compat-annotations",
-        "framework-tethering",
+        "framework-tethering-stubs",
+        "ike-stubs",
     ],
 
     required: [
@@ -43,6 +44,7 @@
         "android.hardware.broadcastradio-V2.0-java",
         "android.hardware.health-V1.0-java",
         "android.hardware.health-V2.0-java",
+        "android.hardware.health-V2.1-java",
         "android.hardware.light-java",
         "android.hardware.weaver-V1.0-java",
         "android.hardware.biometrics.face-V1.0-java",
@@ -54,6 +56,10 @@
         "dnsresolver_aidl_interface-V2-java",
         "netd_event_listener_interface-java",
     ],
+
+    plugins: [
+        "compat-changeid-annotation-processor",
+    ],
 }
 
 java_genrule {
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index bbb7c52..ada1182 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1675,6 +1675,7 @@
             Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
                     | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+                    | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                     | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
             intent.putExtra("time-zone", zone.getID());
             getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 0816955..b676f99 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -26,8 +26,10 @@
 import android.database.ContentObserver;
 import android.hardware.health.V1_0.HealthInfo;
 import android.hardware.health.V2_0.IHealth;
-import android.hardware.health.V2_0.IHealthInfoCallback;
 import android.hardware.health.V2_0.Result;
+import android.hardware.health.V2_1.BatteryCapacityLevel;
+import android.hardware.health.V2_1.Constants;
+import android.hardware.health.V2_1.IHealthInfoCallback;
 import android.hidl.manager.V1_0.IServiceManager;
 import android.hidl.manager.V1_0.IServiceNotification;
 import android.metrics.LogMaker;
@@ -144,6 +146,7 @@
 
     private HealthInfo mHealthInfo;
     private final HealthInfo mLastHealthInfo = new HealthInfo();
+    private android.hardware.health.V2_1.HealthInfo mHealthInfo2p1;
     private boolean mBatteryLevelCritical;
     private int mLastBatteryStatus;
     private int mLastBatteryHealth;
@@ -358,6 +361,9 @@
     }
 
     private boolean shouldShutdownLocked() {
+        if (mHealthInfo2p1.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
+            return (mHealthInfo2p1.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL);
+        }
         if (mHealthInfo.batteryLevel > 0) {
             return false;
         }
@@ -415,22 +421,23 @@
         }
     }
 
-    private void update(android.hardware.health.V2_0.HealthInfo info) {
+    private void update(android.hardware.health.V2_1.HealthInfo info) {
         traceBegin("HealthInfoUpdate");
 
         Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter",
-                info.legacy.batteryChargeCounter);
+                info.legacy.legacy.batteryChargeCounter);
         Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent",
-                info.legacy.batteryCurrent);
+                info.legacy.legacy.batteryCurrent);
 
         synchronized (mLock) {
             if (!mUpdatesStopped) {
-                mHealthInfo = info.legacy;
+                mHealthInfo = info.legacy.legacy;
+                mHealthInfo2p1 = info;
                 // Process the new values.
                 processValuesLocked(false);
                 mLock.notifyAll(); // for any waiters on new info
             } else {
-                copy(mLastHealthInfo, info.legacy);
+                copy(mLastHealthInfo, info.legacy.legacy);
             }
         }
         traceEnd();
@@ -484,7 +491,8 @@
             mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
                     mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature,
                     mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter,
-                    mHealthInfo.batteryFullCharge);
+                    mHealthInfo.batteryFullCharge,
+                    mHealthInfo2p1.batteryChargeTimeToFullNowSeconds);
         } catch (RemoteException e) {
             // Should never happen.
         }
@@ -1120,8 +1128,21 @@
     private final class HealthHalCallback extends IHealthInfoCallback.Stub
             implements HealthServiceWrapper.Callback {
         @Override public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) {
+            android.hardware.health.V2_1.HealthInfo propsLatest =
+                    new android.hardware.health.V2_1.HealthInfo();
+            propsLatest.legacy = props;
+
+            propsLatest.batteryCapacityLevel = BatteryCapacityLevel.UNSUPPORTED;
+            propsLatest.batteryChargeTimeToFullNowSeconds =
+                Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
+
+            BatteryService.this.update(propsLatest);
+        }
+
+        @Override public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) {
             BatteryService.this.update(props);
         }
+
         // on new service registered
         @Override public void onRegistration(IHealth oldService, IHealth newService,
                 String instance) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 450794b..fcdb8a9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1536,7 +1536,8 @@
     }
 
     @Override
-    public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
+    public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
+                int userId, String callingPackageName) {
         // The basic principle is: if an app's traffic could possibly go over a
         // network, without the app doing anything multinetwork-specific,
         // (hence, by "default"), then include that network's capabilities in
@@ -1558,7 +1559,10 @@
         NetworkAgentInfo nai = getDefaultNetwork();
         NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
         if (nc != null) {
-            result.put(nai.network, nc);
+            result.put(
+                    nai.network,
+                    maybeSanitizeLocationInfoForCaller(
+                            nc, Binder.getCallingUid(), callingPackageName));
         }
 
         synchronized (mVpns) {
@@ -1568,10 +1572,12 @@
                     Network[] networks = vpn.getUnderlyingNetworks();
                     if (networks != null) {
                         for (Network network : networks) {
-                            nai = getNetworkAgentInfoForNetwork(network);
-                            nc = getNetworkCapabilitiesInternal(nai);
+                            nc = getNetworkCapabilitiesInternal(network);
                             if (nc != null) {
-                                result.put(network, nc);
+                                result.put(
+                                        network,
+                                        maybeSanitizeLocationInfoForCaller(
+                                                nc, Binder.getCallingUid(), callingPackageName));
                             }
                         }
                     }
@@ -1638,20 +1644,26 @@
         }
     }
 
+    private NetworkCapabilities getNetworkCapabilitiesInternal(Network network) {
+        return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
+    }
+
     private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) {
         if (nai == null) return null;
         synchronized (nai) {
             if (nai.networkCapabilities == null) return null;
             return networkCapabilitiesRestrictedForCallerPermissions(
-                    nai.networkCapabilities,
-                    Binder.getCallingPid(), Binder.getCallingUid());
+                    nai.networkCapabilities, Binder.getCallingPid(), Binder.getCallingUid());
         }
     }
 
     @Override
-    public NetworkCapabilities getNetworkCapabilities(Network network) {
+    public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
+        mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);
         enforceAccessPermission();
-        return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
+        return maybeSanitizeLocationInfoForCaller(
+                getNetworkCapabilitiesInternal(network),
+                Binder.getCallingUid(), callingPackageName);
     }
 
     @VisibleForTesting
@@ -1667,20 +1679,34 @@
         }
         newNc.setAdministratorUids(Collections.EMPTY_LIST);
 
-        maybeSanitizeLocationInfoForCaller(newNc, callerUid);
-
         return newNc;
     }
 
-    private void maybeSanitizeLocationInfoForCaller(
-            NetworkCapabilities nc, int callerUid) {
-        // TODO(b/142072839): Conditionally reset the owner UID if the following
-        // conditions are not met:
-        // 1. The destination app is the network owner
-        // 2. The destination app has the ACCESS_COARSE_LOCATION permission granted
-        // if target SDK<29 or otherwise has the ACCESS_FINE_LOCATION permission granted
-        // 3. The user's location toggle is on
-        nc.setOwnerUid(INVALID_UID);
+    @VisibleForTesting
+    @Nullable
+    NetworkCapabilities maybeSanitizeLocationInfoForCaller(
+            @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) {
+        if (nc == null) {
+            return null;
+        }
+        final NetworkCapabilities newNc = new NetworkCapabilities(nc);
+        if (callerUid != newNc.getOwnerUid()) {
+            newNc.setOwnerUid(INVALID_UID);
+            return newNc;
+        }
+
+        Binder.withCleanCallingIdentity(
+                () -> {
+                    if (!mLocationPermissionChecker.checkLocationPermission(
+                            callerPkgName, null /* featureId */, callerUid, null /* message */)) {
+                        // Caller does not have the requisite location permissions. Reset the
+                        // owner's UID in the NetworkCapabilities.
+                        newNc.setOwnerUid(INVALID_UID);
+                    }
+                }
+        );
+
+        return newNc;
     }
 
     private LinkProperties linkPropertiesRestrictedForCallerPermissions(
@@ -1755,7 +1781,7 @@
     public boolean isActiveNetworkMetered() {
         enforceAccessPermission();
 
-        final NetworkCapabilities caps = getNetworkCapabilities(getActiveNetwork());
+        final NetworkCapabilities caps = getNetworkCapabilitiesInternal(getActiveNetwork());
         if (caps != null) {
             return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         } else {
@@ -3269,7 +3295,6 @@
         for (int i = 0; i < nai.numNetworkRequests(); i++) {
             NetworkRequest request = nai.requestAt(i);
             final NetworkRequestInfo nri = mNetworkRequests.get(request);
-            ensureRunningOnConnectivityServiceThread();
             final NetworkAgentInfo currentNetwork = nri.mSatisfier;
             if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
                 nri.mSatisfier = null;
@@ -3421,7 +3446,6 @@
 
             // If this Network is already the highest scoring Network for a request, or if
             // there is hope for it to become one if it validated, then it is needed.
-            ensureRunningOnConnectivityServiceThread();
             if (nri.request.isRequest() && nai.satisfies(nri.request) &&
                     (nai.isSatisfyingRequest(nri.request.requestId) ||
                     // Note that this catches two important cases:
@@ -3460,7 +3484,6 @@
         if (mNetworkRequests.get(nri.request) == null) {
             return;
         }
-        ensureRunningOnConnectivityServiceThread();
         if (nri.mSatisfier != null) {
             return;
         }
@@ -3498,7 +3521,6 @@
         mNetworkRequestInfoLogs.log("RELEASE " + nri);
         if (nri.request.isRequest()) {
             boolean wasKept = false;
-            ensureRunningOnConnectivityServiceThread();
             final NetworkAgentInfo nai = nri.mSatisfier;
             if (nai != null) {
                 boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
@@ -4783,7 +4805,7 @@
                 return false;
             }
 
-            return vpn.startAlwaysOnVpn();
+            return vpn.startAlwaysOnVpn(mKeyStore);
         }
     }
 
@@ -4798,7 +4820,7 @@
                 Slog.w(TAG, "User " + userId + " has no Vpn configuration");
                 return false;
             }
-            return vpn.isAlwaysOnPackageSupported(packageName);
+            return vpn.isAlwaysOnPackageSupported(packageName, mKeyStore);
         }
     }
 
@@ -4819,11 +4841,11 @@
                 Slog.w(TAG, "User " + userId + " has no Vpn configuration");
                 return false;
             }
-            if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist)) {
+            if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist, mKeyStore)) {
                 return false;
             }
             if (!startAlwaysOnVpn(userId)) {
-                vpn.setAlwaysOnPackage(null, false, null);
+                vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
                 return false;
             }
         }
@@ -5009,7 +5031,7 @@
                 loge("Starting user already has a VPN");
                 return;
             }
-            userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId);
+            userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId, mKeyStore);
             mVpns.put(userId, userVpn);
             if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
                 updateLockdownVpn();
@@ -5080,7 +5102,7 @@
             if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
                 Slog.d(TAG, "Restarting always-on VPN package " + packageName + " for user "
                         + userId);
-                vpn.startAlwaysOnVpn();
+                vpn.startAlwaysOnVpn(mKeyStore);
             }
         }
     }
@@ -5102,7 +5124,7 @@
             if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
                 Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user "
                         + userId);
-                vpn.setAlwaysOnPackage(null, false, null);
+                vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
             }
         }
     }
@@ -5322,8 +5344,8 @@
         }
 
         public String toString() {
-            return "uid/pid:" + mUid + "/" + mPid + " " + request +
-                    (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
+            return "uid/pid:" + mUid + "/" + mPid + " " + request
+                    + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
         }
     }
 
@@ -6416,8 +6438,13 @@
         }
         switch (notificationType) {
             case ConnectivityManager.CALLBACK_AVAILABLE: {
-                putParcelable(bundle, networkCapabilitiesRestrictedForCallerPermissions(
-                        networkAgent.networkCapabilities, nri.mPid, nri.mUid));
+                final NetworkCapabilities nc =
+                        networkCapabilitiesRestrictedForCallerPermissions(
+                                networkAgent.networkCapabilities, nri.mPid, nri.mUid);
+                putParcelable(
+                        bundle,
+                        maybeSanitizeLocationInfoForCaller(
+                                nc, nri.mUid, nri.request.getRequestorPackageName()));
                 putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
                         networkAgent.linkProperties, nri.mPid, nri.mUid));
                 // For this notification, arg1 contains the blocked status.
@@ -6430,9 +6457,13 @@
             }
             case ConnectivityManager.CALLBACK_CAP_CHANGED: {
                 // networkAgent can't be null as it has been accessed a few lines above.
-                final NetworkCapabilities nc = networkCapabilitiesRestrictedForCallerPermissions(
-                        networkAgent.networkCapabilities, nri.mPid, nri.mUid);
-                putParcelable(bundle, nc);
+                final NetworkCapabilities netCap =
+                        networkCapabilitiesRestrictedForCallerPermissions(
+                                networkAgent.networkCapabilities, nri.mPid, nri.mUid);
+                putParcelable(
+                        bundle,
+                        maybeSanitizeLocationInfoForCaller(
+                                netCap, nri.mUid, nri.request.getRequestorPackageName()));
                 break;
             }
             case ConnectivityManager.CALLBACK_IP_CHANGED: {
@@ -7528,6 +7559,13 @@
      */
     public int getConnectionOwnerUid(ConnectionInfo connectionInfo) {
         final Vpn vpn = enforceActiveVpnOrNetworkStackPermission();
+
+        // Only VpnService based VPNs should be able to get this information.
+        if (vpn != null && vpn.getActiveAppVpnType() != VpnManager.TYPE_VPN_SERVICE) {
+            throw new SecurityException(
+                    "getConnectionOwnerUid() not allowed for non-VpnService VPNs");
+        }
+
         if (connectionInfo.protocol != IPPROTO_TCP && connectionInfo.protocol != IPPROTO_UDP) {
             throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol);
         }
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index c60460f..41207c9 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.gsi.AvbPublicKey;
 import android.gsi.GsiProgress;
 import android.gsi.IGsiService;
 import android.gsi.IGsid;
@@ -227,4 +228,13 @@
             throw new RuntimeException(e.toString());
         }
     }
+
+    @Override
+    public boolean getAvbPublicKey(AvbPublicKey dst) {
+        try {
+            return getGsiService().getAvbPublicKey(dst) == 0;
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index f0a3bfd..af7af5f 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1357,7 +1357,7 @@
     public void notifyCarrierNetworkChange(boolean active) {
         // only CarrierService with carrier privilege rule should have the permission
         int[] subIds = Arrays.stream(SubscriptionManager.from(mContext)
-                    .getActiveSubscriptionIdList(false))
+                    .getActiveAndHiddenSubscriptionIdList())
                     .filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(mContext,
                             i)).toArray();
         if (ArrayUtils.isEmpty(subIds)) {
diff --git a/services/core/java/com/android/server/UserspaceRebootLogger.java b/services/core/java/com/android/server/UserspaceRebootLogger.java
new file mode 100644
index 0000000..74f113f
--- /dev/null
+++ b/services/core/java/com/android/server/UserspaceRebootLogger.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 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;
+
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED;
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__LOCKED;
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__UNLOCKED;
+
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Utility class to help abstract logging {@code UserspaceRebootReported} atom.
+ */
+public final class UserspaceRebootLogger {
+
+    private static final String TAG = "UserspaceRebootLogger";
+
+    private static final String USERSPACE_REBOOT_SHOULD_LOG_PROPERTY =
+            "persist.sys.userspace_reboot.log.should_log";
+    private static final String USERSPACE_REBOOT_LAST_STARTED_PROPERTY =
+            "sys.userspace_reboot.log.last_started";
+    private static final String USERSPACE_REBOOT_LAST_FINISHED_PROPERTY =
+            "sys.userspace_reboot.log.last_finished";
+    private static final String BOOT_REASON_PROPERTY = "sys.boot.reason";
+
+    private UserspaceRebootLogger() {}
+
+    /**
+     * Modifies internal state to note that {@code UserspaceRebootReported} atom needs to be
+     * logged on the next successful boot.
+     */
+    public static void noteUserspaceRebootWasRequested() {
+        SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, "1");
+        SystemProperties.set(USERSPACE_REBOOT_LAST_STARTED_PROPERTY,
+                String.valueOf(SystemClock.elapsedRealtime()));
+    }
+
+    /**
+     * Updates internal state on boot after successful userspace reboot.
+     *
+     * <p>Should be called right before framework sets {@code sys.boot_completed} property.
+     */
+    public static void noteUserspaceRebootSuccess() {
+        SystemProperties.set(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY,
+                String.valueOf(SystemClock.elapsedRealtime()));
+    }
+
+    /**
+     * Returns {@code true} if {@code UserspaceRebootReported} atom should be logged.
+     */
+    public static boolean shouldLogUserspaceRebootEvent() {
+        return SystemProperties.getBoolean(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, false);
+    }
+
+    /**
+     * Asynchronously logs {@code UserspaceRebootReported} on the given {@code executor}.
+     *
+     * <p>Should be called in the end of {@link
+     * com.android.server.am.ActivityManagerService#finishBooting()} method, after framework have
+     * tried to proactivelly unlock storage of the primary user.
+     */
+    public static void logEventAsync(boolean userUnlocked, Executor executor) {
+        final int outcome = computeOutcome();
+        final long durationMillis;
+        if (outcome == USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS) {
+            durationMillis = SystemProperties.getLong(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY, 0)
+                    - SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, 0);
+        } else {
+            durationMillis = 0;
+        }
+        final int encryptionState =
+                userUnlocked
+                    ? USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__UNLOCKED
+                    : USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__LOCKED;
+        executor.execute(
+                () -> {
+                    Slog.i(TAG, "Logging UserspaceRebootReported atom: { outcome: " + outcome
+                            + " durationMillis: " + durationMillis + " encryptionState: "
+                            + encryptionState + " }");
+                    FrameworkStatsLog.write(FrameworkStatsLog.USERSPACE_REBOOT_REPORTED, outcome,
+                            durationMillis, encryptionState);
+                    SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, "");
+                });
+    }
+
+    private static int computeOutcome() {
+        if (SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, -1) != -1) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS;
+        }
+        String reason = SystemProperties.get(BOOT_REASON_PROPERTY, "");
+        if (reason.startsWith("reboot,")) {
+            reason = reason.substring("reboot".length());
+        }
+        switch (reason) {
+            case "userspace_failed,watchdog_fork":
+                // Since fork happens before shutdown sequence, attribute it to
+                // USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED.
+            case "userspace_failed,shutdown_aborted":
+                return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
+            case "userspace_failed,init_user0_failed":
+                // init_user0 will fail if userdata wasn't remounted correctly, attribute to
+                // USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT.
+            case "mount_userdata_failed":
+                return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
+            case "userspace_failed,watchdog_triggered":
+                return
+                    USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED;
+            default:
+                return USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 4b48ef9..4a6c2be 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -20,18 +20,37 @@
 
 import android.annotation.TestApi;
 import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.debug.AdbManager;
 import android.debug.AdbProtoEnums;
+import android.debug.AdbTransportType;
+import android.debug.PairDevice;
+import android.net.ConnectivityManager;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
+import android.net.NetworkInfo;
 import android.net.Uri;
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -51,6 +70,8 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.dump.DualDumpOutputStream;
@@ -70,6 +91,8 @@
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -78,6 +101,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Provides communication to the Android Debug Bridge daemon to allow, deny, or clear public keysi
@@ -86,6 +110,7 @@
 public class AdbDebuggingManager {
     private static final String TAG = "AdbDebuggingManager";
     private static final boolean DEBUG = false;
+    private static final boolean MDNS_DEBUG = false;
 
     private static final String ADBD_SOCKET = "adbd";
     private static final String ADB_DIRECTORY = "misc/adb";
@@ -97,19 +122,39 @@
     private static final int BUFFER_SIZE = 65536;
 
     private final Context mContext;
+    private final ContentResolver mContentResolver;
     private final Handler mHandler;
     private AdbDebuggingThread mThread;
-    private boolean mAdbEnabled = false;
+    private boolean mAdbUsbEnabled = false;
+    private boolean mAdbWifiEnabled = false;
     private String mFingerprints;
-    private final List<String> mConnectedKeys;
+    // A key can be used more than once (e.g. USB, wifi), so need to keep a refcount
+    private final Map<String, Integer> mConnectedKeys;
     private String mConfirmComponent;
     private final File mTestUserKeyFile;
 
+    private static final String WIFI_PERSISTENT_CONFIG_PROPERTY =
+            "persist.adb.tls_server.enable";
+    private static final String WIFI_PERSISTENT_GUID =
+            "persist.adb.wifi.guid";
+    private static final int PAIRING_CODE_LENGTH = 6;
+    private PairingThread mPairingThread = null;
+    // A list of keys connected via wifi
+    private final Set<String> mWifiConnectedKeys;
+    // The current info of the adbwifi connection.
+    private AdbConnectionInfo mAdbConnectionInfo;
+    // Polls for a tls port property when adb wifi is enabled
+    private AdbConnectionPortPoller mConnectionPortPoller;
+    private final PortListenerImpl mPortListener = new PortListenerImpl();
+
     public AdbDebuggingManager(Context context) {
         mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
         mContext = context;
+        mContentResolver = mContext.getContentResolver();
         mTestUserKeyFile = null;
-        mConnectedKeys = new ArrayList<>(1);
+        mConnectedKeys = new HashMap<String, Integer>();
+        mWifiConnectedKeys = new HashSet<String>();
+        mAdbConnectionInfo = new AdbConnectionInfo();
     }
 
     /**
@@ -120,9 +165,178 @@
     protected AdbDebuggingManager(Context context, String confirmComponent, File testUserKeyFile) {
         mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
         mContext = context;
+        mContentResolver = mContext.getContentResolver();
         mConfirmComponent = confirmComponent;
         mTestUserKeyFile = testUserKeyFile;
-        mConnectedKeys = new ArrayList<>();
+        mConnectedKeys = new HashMap<String, Integer>();
+        mWifiConnectedKeys = new HashSet<String>();
+        mAdbConnectionInfo = new AdbConnectionInfo();
+    }
+
+    class PairingThread extends Thread implements NsdManager.RegistrationListener {
+        private NsdManager mNsdManager;
+        private String mPublicKey;
+        private String mPairingCode;
+        private String mGuid;
+        private String mServiceName;
+        private final String mServiceType = "_adb_secure_pairing._tcp.";
+        private int mPort;
+
+        private native int native_pairing_start(String guid, String password);
+        private native void native_pairing_cancel();
+        private native boolean native_pairing_wait();
+
+        PairingThread(String pairingCode, String serviceName) {
+            super(TAG);
+            mPairingCode = pairingCode;
+            mGuid = SystemProperties.get(WIFI_PERSISTENT_GUID);
+            mServiceName = serviceName;
+            if (serviceName == null || serviceName.isEmpty()) {
+                mServiceName = mGuid;
+            }
+            mPort = -1;
+            mNsdManager = (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
+        }
+
+        @Override
+        public void run() {
+            if (mGuid.isEmpty()) {
+                Slog.e(TAG, "adbwifi guid was not set");
+                return;
+            }
+            mPort = native_pairing_start(mGuid, mPairingCode);
+            if (mPort <= 0 || mPort > 65535) {
+                Slog.e(TAG, "Unable to start pairing server");
+                return;
+            }
+
+            // Register the mdns service
+            NsdServiceInfo serviceInfo = new NsdServiceInfo();
+            serviceInfo.setServiceName(mServiceName);
+            serviceInfo.setServiceType(mServiceType);
+            serviceInfo.setPort(mPort);
+            mNsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, this);
+
+            // Send pairing port to UI
+            Message msg = mHandler.obtainMessage(
+                    AdbDebuggingHandler.MSG_RESPONSE_PAIRING_PORT);
+            msg.obj = mPort;
+            mHandler.sendMessage(msg);
+
+            boolean paired = native_pairing_wait();
+            if (DEBUG) {
+                if (mPublicKey != null) {
+                    Slog.i(TAG, "Pairing succeeded key=" + mPublicKey);
+                } else {
+                    Slog.i(TAG, "Pairing failed");
+                }
+            }
+
+            mNsdManager.unregisterService(this);
+
+            Bundle bundle = new Bundle();
+            bundle.putString("publicKey", paired ? mPublicKey : null);
+            Message message = Message.obtain(mHandler,
+                                             AdbDebuggingHandler.MSG_RESPONSE_PAIRING_RESULT,
+                                             bundle);
+            mHandler.sendMessage(message);
+        }
+
+        public void cancelPairing() {
+            native_pairing_cancel();
+        }
+
+        @Override
+        public void onServiceRegistered(NsdServiceInfo serviceInfo) {
+            if (MDNS_DEBUG) Slog.i(TAG, "Registered pairing service: " + serviceInfo);
+        }
+
+        @Override
+        public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
+            Slog.e(TAG, "Failed to register pairing service(err=" + errorCode
+                    + "): " + serviceInfo);
+            cancelPairing();
+        }
+
+        @Override
+        public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
+            if (MDNS_DEBUG) Slog.i(TAG, "Unregistered pairing service: " + serviceInfo);
+        }
+
+        @Override
+        public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
+            Slog.w(TAG, "Failed to unregister pairing service(err=" + errorCode
+                    + "): " + serviceInfo);
+        }
+    }
+
+    interface AdbConnectionPortListener {
+        void onPortReceived(int port);
+    }
+
+    /**
+     * This class will poll for a period of time for adbd to write the port
+     * it connected to.
+     *
+     * TODO(joshuaduong): The port is being sent via system property because the adbd socket
+     * (AdbDebuggingManager) is not created when ro.adb.secure=0. Thus, we must communicate the
+     * port through different means. A better fix would be to always start AdbDebuggingManager, but
+     * it needs to adjust accordingly on whether ro.adb.secure is set.
+     */
+    static class AdbConnectionPortPoller extends Thread {
+        private final String mAdbPortProp = "service.adb.tls.port";
+        private AdbConnectionPortListener mListener;
+        private final int mDurationSecs = 10;
+        private AtomicBoolean mCanceled = new AtomicBoolean(false);
+
+        AdbConnectionPortPoller(AdbConnectionPortListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG) Slog.d(TAG, "Starting adb port property poller");
+            // Once adbwifi is enabled, we poll the service.adb.tls.port
+            // system property until we get the port, or -1 on failure.
+            // Let's also limit the polling to 10 seconds, just in case
+            // something went wrong.
+            for (int i = 0; i < mDurationSecs; ++i) {
+                if (mCanceled.get()) {
+                    return;
+                }
+
+                // If the property is set to -1, then that means adbd has failed
+                // to start the server. Otherwise we should have a valid port.
+                int port = SystemProperties.getInt(mAdbPortProp, Integer.MAX_VALUE);
+                if (port == -1 || (port > 0 && port <= 65535)) {
+                    mListener.onPortReceived(port);
+                    return;
+                }
+                SystemClock.sleep(1000);
+            }
+            Slog.w(TAG, "Failed to receive adb connection port");
+            mListener.onPortReceived(-1);
+        }
+
+        public void cancelAndWait() {
+            mCanceled.set(true);
+            if (this.isAlive()) {
+                try {
+                    this.join();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+
+    class PortListenerImpl implements AdbConnectionPortListener {
+        public void onPortReceived(int port) {
+            Message msg = mHandler.obtainMessage(port > 0
+                     ? AdbDebuggingHandler.MSG_SERVER_CONNECTED
+                     : AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
+            msg.obj = port;
+            mHandler.sendMessage(msg);
+        }
     }
 
     class AdbDebuggingThread extends Thread {
@@ -212,6 +426,46 @@
                                 AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY);
                         msg.obj = key;
                         mHandler.sendMessage(msg);
+                    } else if (buffer[0] == 'W' && buffer[1] == 'E') {
+                        // adbd_auth.h and AdbTransportType.aidl need to be kept in
+                        // sync.
+                        byte transportType = buffer[2];
+                        String key = new String(Arrays.copyOfRange(buffer, 3, count));
+                        if (transportType == AdbTransportType.USB) {
+                            Slog.d(TAG, "Received USB TLS connected key message: " + key);
+                            Message msg = mHandler.obtainMessage(
+                                    AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY);
+                            msg.obj = key;
+                            mHandler.sendMessage(msg);
+                        } else if (transportType == AdbTransportType.WIFI) {
+                            Slog.d(TAG, "Received WIFI TLS connected key message: " + key);
+                            Message msg = mHandler.obtainMessage(
+                                    AdbDebuggingHandler.MSG_WIFI_DEVICE_CONNECTED);
+                            msg.obj = key;
+                            mHandler.sendMessage(msg);
+                        } else {
+                            Slog.e(TAG, "Got unknown transport type from adbd (" + transportType
+                                    + ")");
+                        }
+                    } else if (buffer[0] == 'W' && buffer[1] == 'F') {
+                        byte transportType = buffer[2];
+                        String key = new String(Arrays.copyOfRange(buffer, 3, count));
+                        if (transportType == AdbTransportType.USB) {
+                            Slog.d(TAG, "Received USB TLS disconnect message: " + key);
+                            Message msg = mHandler.obtainMessage(
+                                    AdbDebuggingHandler.MESSAGE_ADB_DISCONNECT);
+                            msg.obj = key;
+                            mHandler.sendMessage(msg);
+                        } else if (transportType == AdbTransportType.WIFI) {
+                            Slog.d(TAG, "Received WIFI TLS disconnect key message: " + key);
+                            Message msg = mHandler.obtainMessage(
+                                    AdbDebuggingHandler.MSG_WIFI_DEVICE_DISCONNECTED);
+                            msg.obj = key;
+                            mHandler.sendMessage(msg);
+                        } else {
+                            Slog.e(TAG, "Got unknown transport type from adbd (" + transportType
+                                    + ")");
+                        }
                     } else {
                         Slog.e(TAG, "Wrong message: "
                                 + (new String(Arrays.copyOfRange(buffer, 0, 2))));
@@ -267,7 +521,156 @@
         }
     }
 
+    class AdbConnectionInfo {
+        private String mBssid;
+        private String mSsid;
+        private int mPort;
+
+        AdbConnectionInfo() {
+            mBssid = "";
+            mSsid = "";
+            mPort = -1;
+        }
+
+        AdbConnectionInfo(String bssid, String ssid) {
+            mBssid = bssid;
+            mSsid = ssid;
+        }
+
+        AdbConnectionInfo(AdbConnectionInfo other) {
+            mBssid = other.mBssid;
+            mSsid = other.mSsid;
+            mPort = other.mPort;
+        }
+
+        public String getBSSID() {
+            return mBssid;
+        }
+
+        public String getSSID() {
+            return mSsid;
+        }
+
+        public int getPort() {
+            return mPort;
+        }
+
+        public void setPort(int port) {
+            mPort = port;
+        }
+
+        public void clear() {
+            mBssid = "";
+            mSsid = "";
+            mPort = -1;
+        }
+    }
+
+    private void setAdbConnectionInfo(AdbConnectionInfo info) {
+        synchronized (mAdbConnectionInfo) {
+            if (info == null) {
+                mAdbConnectionInfo.clear();
+                return;
+            }
+            mAdbConnectionInfo = info;
+        }
+    }
+
+    private AdbConnectionInfo getAdbConnectionInfo() {
+        synchronized (mAdbConnectionInfo) {
+            return new AdbConnectionInfo(mAdbConnectionInfo);
+        }
+    }
+
     class AdbDebuggingHandler extends Handler {
+        private NotificationManager mNotificationManager;
+        private boolean mAdbNotificationShown;
+
+        private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                // We only care about when wifi is disabled, and when there is a wifi network
+                // change.
+                if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
+                    int state = intent.getIntExtra(
+                            WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
+                    if (state == WifiManager.WIFI_STATE_DISABLED) {
+                        Slog.i(TAG, "Wifi disabled. Disabling adbwifi.");
+                        Settings.Global.putInt(mContentResolver,
+                                Settings.Global.ADB_WIFI_ENABLED, 0);
+                    }
+                } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+                    // We only care about wifi type connections
+                    NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
+                            WifiManager.EXTRA_NETWORK_INFO);
+                    if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+                        // Check for network disconnect
+                        if (!networkInfo.isConnected()) {
+                            Slog.i(TAG, "Network disconnected. Disabling adbwifi.");
+                            Settings.Global.putInt(mContentResolver,
+                                    Settings.Global.ADB_WIFI_ENABLED, 0);
+                            return;
+                        }
+
+                        WifiManager wifiManager =
+                                (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+                        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+                        if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+                            Slog.i(TAG, "Not connected to any wireless network."
+                                    + " Not enabling adbwifi.");
+                            Settings.Global.putInt(mContentResolver,
+                                    Settings.Global.ADB_WIFI_ENABLED, 0);
+                        }
+
+                        // Check for network change
+                        String bssid = wifiInfo.getBSSID();
+                        if (bssid == null || bssid.isEmpty()) {
+                            Slog.e(TAG, "Unable to get the wifi ap's BSSID. Disabling adbwifi.");
+                            Settings.Global.putInt(mContentResolver,
+                                    Settings.Global.ADB_WIFI_ENABLED, 0);
+                        }
+                        synchronized (mAdbConnectionInfo) {
+                            if (!bssid.equals(mAdbConnectionInfo.getBSSID())) {
+                                Slog.i(TAG, "Detected wifi network change. Disabling adbwifi.");
+                                Settings.Global.putInt(mContentResolver,
+                                        Settings.Global.ADB_WIFI_ENABLED, 0);
+                            }
+                        }
+                    }
+                }
+            }
+        };
+
+        private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
+
+        private boolean isTv() {
+            return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+        }
+
+        private void setupNotifications() {
+            if (mNotificationManager != null) {
+                return;
+            }
+            mNotificationManager = (NotificationManager)
+                    mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+            if (mNotificationManager == null) {
+                Slog.e(TAG, "Unable to setup notifications for wireless debugging");
+                return;
+            }
+
+            // Ensure that the notification channels are set up
+            if (isTv()) {
+                // TV-specific notification channel
+                mNotificationManager.createNotificationChannel(
+                        new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV,
+                                mContext.getString(
+                                        com.android.internal.R.string
+                                                .adb_debugging_notification_channel_tv),
+                                NotificationManager.IMPORTANCE_HIGH));
+            }
+        }
+
         // The default time to schedule the job to keep the keystore updated with a currently
         // connected key as well as to removed expired keys.
         static final long UPDATE_KEYSTORE_JOB_INTERVAL = 86400000;
@@ -287,8 +690,50 @@
         static final int MESSAGE_ADB_UPDATE_KEYSTORE = 9;
         static final int MESSAGE_ADB_CONNECTED_KEY = 10;
 
+        // === Messages from the UI ==============
+        // UI asks adbd to enable adbdwifi
+        static final int MSG_ADBDWIFI_ENABLE = 11;
+        // UI asks adbd to disable adbdwifi
+        static final int MSG_ADBDWIFI_DISABLE = 12;
+        // Cancel pairing
+        static final int MSG_PAIRING_CANCEL = 14;
+        // Enable pairing by pairing code
+        static final int MSG_PAIR_PAIRING_CODE = 15;
+        // Enable pairing by QR code
+        static final int MSG_PAIR_QR_CODE = 16;
+        // UI asks to unpair (forget) a device.
+        static final int MSG_REQ_UNPAIR = 17;
+        // User allows debugging on the current network
+        static final int MSG_ADBWIFI_ALLOW = 18;
+        // User denies debugging on the current network
+        static final int MSG_ADBWIFI_DENY = 19;
+
+        // === Messages from the PairingThread ===========
+        // Result of the pairing
+        static final int MSG_RESPONSE_PAIRING_RESULT = 20;
+        // The port opened for pairing
+        static final int MSG_RESPONSE_PAIRING_PORT = 21;
+
+        // === Messages from adbd ================
+        // Notifies us a wifi device connected.
+        static final int MSG_WIFI_DEVICE_CONNECTED = 22;
+        // Notifies us a wifi device disconnected.
+        static final int MSG_WIFI_DEVICE_DISCONNECTED = 23;
+        // Notifies us the TLS server is connected and listening
+        static final int MSG_SERVER_CONNECTED = 24;
+        // Notifies us the TLS server is disconnected
+        static final int MSG_SERVER_DISCONNECTED = 25;
+
+        // === Messages we can send to adbd ===========
+        static final String MSG_DISCONNECT_DEVICE = "DD";
+        static final String MSG_DISABLE_ADBDWIFI = "DA";
+
         private AdbKeyStore mAdbKeyStore;
 
+        // Usb, Wi-Fi transports can be enabled together or separately, so don't break the framework
+        // connection unless all transport types are disconnected.
+        private int mAdbEnabledRefCount = 0;
+
         private ContentObserver mAuthTimeObserver = new ContentObserver(this) {
             @Override
             public void onChange(boolean selfChange, Uri uri) {
@@ -313,44 +758,111 @@
             mAdbKeyStore = adbKeyStore;
         }
 
+        // Show when at least one device is connected.
+        public void showAdbConnectedNotification(boolean show) {
+            final int id = SystemMessage.NOTE_ADB_WIFI_ACTIVE;
+            final int titleRes = com.android.internal.R.string.adbwifi_active_notification_title;
+            if (show == mAdbNotificationShown) {
+                return;
+            }
+            setupNotifications();
+            if (!mAdbNotificationShown) {
+                Resources r = mContext.getResources();
+                CharSequence title = r.getText(titleRes);
+                CharSequence message = r.getText(
+                        com.android.internal.R.string.adbwifi_active_notification_message);
+
+                Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+                PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
+                        intent, 0, null, UserHandle.CURRENT);
+
+                Notification notification =
+                        new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER)
+                                .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+                                .setWhen(0)
+                                .setOngoing(true)
+                                .setTicker(title)
+                                .setDefaults(0)  // please be quiet
+                                .setColor(mContext.getColor(
+                                        com.android.internal.R.color
+                                                .system_notification_accent_color))
+                                .setContentTitle(title)
+                                .setContentText(message)
+                                .setContentIntent(pi)
+                                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                                .extend(new Notification.TvExtender()
+                                        .setChannelId(ADB_NOTIFICATION_CHANNEL_ID_TV))
+                                .build();
+                mAdbNotificationShown = true;
+                mNotificationManager.notifyAsUser(null, id, notification,
+                        UserHandle.ALL);
+            } else {
+                mAdbNotificationShown = false;
+                mNotificationManager.cancelAsUser(null, id, UserHandle.ALL);
+            }
+        }
+
+        private void startAdbDebuggingThread() {
+            ++mAdbEnabledRefCount;
+            if (DEBUG) Slog.i(TAG, "startAdbDebuggingThread ref=" + mAdbEnabledRefCount);
+            if (mAdbEnabledRefCount > 1) {
+                return;
+            }
+
+            registerForAuthTimeChanges();
+            mThread = new AdbDebuggingThread();
+            mThread.start();
+
+            mAdbKeyStore.updateKeyStore();
+            scheduleJobToUpdateAdbKeyStore();
+        }
+
+        private void stopAdbDebuggingThread() {
+            --mAdbEnabledRefCount;
+            if (DEBUG) Slog.i(TAG, "stopAdbDebuggingThread ref=" + mAdbEnabledRefCount);
+            if (mAdbEnabledRefCount > 0) {
+                return;
+            }
+
+            if (mThread != null) {
+                mThread.stopListening();
+                mThread = null;
+            }
+
+            if (!mConnectedKeys.isEmpty()) {
+                for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
+                    mAdbKeyStore.setLastConnectionTime(entry.getKey(),
+                            System.currentTimeMillis());
+                }
+                sendPersistKeyStoreMessage();
+                mConnectedKeys.clear();
+                mWifiConnectedKeys.clear();
+            }
+            scheduleJobToUpdateAdbKeyStore();
+        }
+
         public void handleMessage(Message msg) {
+            if (mAdbKeyStore == null) {
+                mAdbKeyStore = new AdbKeyStore();
+            }
+
             switch (msg.what) {
                 case MESSAGE_ADB_ENABLED:
-                    if (mAdbEnabled) {
+                    if (mAdbUsbEnabled) {
                         break;
                     }
-                    registerForAuthTimeChanges();
-                    mAdbEnabled = true;
-
-                    mThread = new AdbDebuggingThread();
-                    mThread.start();
-
-                    mAdbKeyStore = new AdbKeyStore();
-                    mAdbKeyStore.updateKeyStore();
-                    scheduleJobToUpdateAdbKeyStore();
+                    startAdbDebuggingThread();
+                    mAdbUsbEnabled = true;
                     break;
 
                 case MESSAGE_ADB_DISABLED:
-                    if (!mAdbEnabled) {
+                    if (!mAdbUsbEnabled) {
                         break;
                     }
-
-                    mAdbEnabled = false;
-
-                    if (mThread != null) {
-                        mThread.stopListening();
-                        mThread = null;
-                    }
-
-                    if (!mConnectedKeys.isEmpty()) {
-                        for (String connectedKey : mConnectedKeys) {
-                            mAdbKeyStore.setLastConnectionTime(connectedKey,
-                                    System.currentTimeMillis());
-                        }
-                        sendPersistKeyStoreMessage();
-                        mConnectedKeys.clear();
-                    }
-                    scheduleJobToUpdateAdbKeyStore();
+                    stopAdbDebuggingThread();
+                    mAdbUsbEnabled = false;
                     break;
 
                 case MESSAGE_ADB_ALLOW: {
@@ -366,8 +878,8 @@
                     if (mThread != null) {
                         mThread.sendResponse("OK");
                         if (alwaysAllow) {
-                            if (!mConnectedKeys.contains(key)) {
-                                mConnectedKeys.add(key);
+                            if (!mConnectedKeys.containsKey(key)) {
+                                mConnectedKeys.put(key, 1);
                             }
                             mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
                             sendPersistKeyStoreMessage();
@@ -406,13 +918,14 @@
                     }
                     logAdbConnectionChanged(key, AdbProtoEnums.AWAITING_USER_APPROVAL, false);
                     mFingerprints = fingerprints;
-                    startConfirmation(key, mFingerprints);
+                    startConfirmationForKey(key, mFingerprints);
                     break;
                 }
 
                 case MESSAGE_ADB_CLEAR: {
                     Slog.d(TAG, "Received a request to clear the adb authorizations");
                     mConnectedKeys.clear();
+                    mWifiConnectedKeys.clear();
                     mAdbKeyStore.deleteKeyStore();
                     cancelJobToUpdateAdbKeyStore();
                     break;
@@ -422,12 +935,17 @@
                     String key = (String) msg.obj;
                     boolean alwaysAllow = false;
                     if (key != null && key.length() > 0) {
-                        if (mConnectedKeys.contains(key)) {
+                        if (mConnectedKeys.containsKey(key)) {
                             alwaysAllow = true;
-                            mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
-                            sendPersistKeyStoreMessage();
-                            scheduleJobToUpdateAdbKeyStore();
-                            mConnectedKeys.remove(key);
+                            int refcount = mConnectedKeys.get(key) - 1;
+                            if (refcount == 0) {
+                                mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
+                                sendPersistKeyStoreMessage();
+                                scheduleJobToUpdateAdbKeyStore();
+                                mConnectedKeys.remove(key);
+                            } else {
+                                mConnectedKeys.put(key, refcount);
+                            }
                         }
                     } else {
                         Slog.w(TAG, "Received a disconnected key message with an empty key");
@@ -445,8 +963,8 @@
 
                 case MESSAGE_ADB_UPDATE_KEYSTORE: {
                     if (!mConnectedKeys.isEmpty()) {
-                        for (String connectedKey : mConnectedKeys) {
-                            mAdbKeyStore.setLastConnectionTime(connectedKey,
+                        for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
+                            mAdbKeyStore.setLastConnectionTime(entry.getKey(),
                                     System.currentTimeMillis());
                         }
                         sendPersistKeyStoreMessage();
@@ -463,8 +981,10 @@
                     if (key == null || key.length() == 0) {
                         Slog.w(TAG, "Received a connected key message with an empty key");
                     } else {
-                        if (!mConnectedKeys.contains(key)) {
-                            mConnectedKeys.add(key);
+                        if (!mConnectedKeys.containsKey(key)) {
+                            mConnectedKeys.put(key, 1);
+                        } else {
+                            mConnectedKeys.put(key, mConnectedKeys.get(key) + 1);
                         }
                         mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
                         sendPersistKeyStoreMessage();
@@ -473,6 +993,199 @@
                     }
                     break;
                 }
+                case MSG_ADBDWIFI_ENABLE: {
+                    if (mAdbWifiEnabled) {
+                        break;
+                    }
+
+                    AdbConnectionInfo currentInfo = getCurrentWifiApInfo();
+                    if (currentInfo == null) {
+                        Settings.Global.putInt(mContentResolver,
+                                Settings.Global.ADB_WIFI_ENABLED, 0);
+                        break;
+                    }
+
+                    if (!verifyWifiNetwork(currentInfo.getBSSID(),
+                            currentInfo.getSSID())) {
+                        // This means that the network is not in the list of trusted networks.
+                        // We'll give user a prompt on whether to allow wireless debugging on
+                        // the current wifi network.
+                        Settings.Global.putInt(mContentResolver,
+                                Settings.Global.ADB_WIFI_ENABLED, 0);
+                        break;
+                    }
+
+                    setAdbConnectionInfo(currentInfo);
+                    IntentFilter intentFilter =
+                            new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+                    intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+                    mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+                    SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+                    mConnectionPortPoller =
+                            new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+                    mConnectionPortPoller.start();
+
+                    startAdbDebuggingThread();
+                    mAdbWifiEnabled = true;
+
+                    if (DEBUG) Slog.i(TAG, "adb start wireless adb");
+                    break;
+                }
+                case MSG_ADBDWIFI_DISABLE:
+                    if (!mAdbWifiEnabled) {
+                        break;
+                    }
+                    mAdbWifiEnabled = false;
+                    setAdbConnectionInfo(null);
+                    mContext.unregisterReceiver(mBroadcastReceiver);
+
+                    if (mThread != null) {
+                        mThread.sendResponse(MSG_DISABLE_ADBDWIFI);
+                    }
+                    onAdbdWifiServerDisconnected(-1);
+                    stopAdbDebuggingThread();
+                    break;
+                case MSG_ADBWIFI_ALLOW:
+                    if (mAdbWifiEnabled) {
+                        break;
+                    }
+                    String bssid = (String) msg.obj;
+                    boolean alwaysAllow = msg.arg1 == 1;
+                    if (alwaysAllow) {
+                        mAdbKeyStore.addTrustedNetwork(bssid);
+                    }
+
+                    // Let's check again to make sure we didn't switch networks while verifying
+                    // the wifi bssid.
+                    AdbConnectionInfo newInfo = getCurrentWifiApInfo();
+                    if (newInfo == null || !bssid.equals(newInfo.getBSSID())) {
+                        break;
+                    }
+
+                    setAdbConnectionInfo(newInfo);
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 1);
+                    IntentFilter intentFilter =
+                            new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+                    intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+                    mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+                    SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+                    mConnectionPortPoller =
+                            new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+                    mConnectionPortPoller.start();
+
+                    startAdbDebuggingThread();
+                    mAdbWifiEnabled = true;
+
+                    if (DEBUG) Slog.i(TAG, "adb start wireless adb");
+                    break;
+                case MSG_ADBWIFI_DENY:
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 0);
+                    sendServerConnectionState(false, -1);
+                    break;
+                case MSG_REQ_UNPAIR: {
+                    String fingerprint = (String) msg.obj;
+                    // Tell adbd to disconnect the device if connected.
+                    String publicKey = mAdbKeyStore.findKeyFromFingerprint(fingerprint);
+                    if (publicKey == null || publicKey.isEmpty()) {
+                        Slog.e(TAG, "Not a known fingerprint [" + fingerprint + "]");
+                        break;
+                    }
+                    String cmdStr = MSG_DISCONNECT_DEVICE + publicKey;
+                    if (mThread != null) {
+                        mThread.sendResponse(cmdStr);
+                    }
+                    mAdbKeyStore.removeKey(publicKey);
+                    // Send the updated paired devices list to the UI.
+                    sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+                    break;
+                }
+                case MSG_RESPONSE_PAIRING_RESULT: {
+                    Bundle bundle = (Bundle) msg.obj;
+                    String publicKey = bundle.getString("publicKey");
+                    onPairingResult(publicKey);
+                    // Send the updated paired devices list to the UI.
+                    sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+                    break;
+                }
+                case MSG_RESPONSE_PAIRING_PORT: {
+                    int port = (int) msg.obj;
+                    sendPairingPortToUI(port);
+                    break;
+                }
+                case MSG_PAIR_PAIRING_CODE: {
+                    String pairingCode = createPairingCode(PAIRING_CODE_LENGTH);
+                    updateUIPairCode(pairingCode);
+                    mPairingThread = new PairingThread(pairingCode, null);
+                    mPairingThread.start();
+                    break;
+                }
+                case MSG_PAIR_QR_CODE: {
+                    Bundle bundle = (Bundle) msg.obj;
+                    String serviceName = bundle.getString("serviceName");
+                    String password = bundle.getString("password");
+                    mPairingThread = new PairingThread(password, serviceName);
+                    mPairingThread.start();
+                    break;
+                }
+                case MSG_PAIRING_CANCEL:
+                    if (mPairingThread != null) {
+                        mPairingThread.cancelPairing();
+                        try {
+                            mPairingThread.join();
+                        } catch (InterruptedException e) {
+                            Slog.w(TAG, "Error while waiting for pairing thread to quit.");
+                            e.printStackTrace();
+                        }
+                        mPairingThread = null;
+                    }
+                    break;
+                case MSG_WIFI_DEVICE_CONNECTED: {
+                    String key = (String) msg.obj;
+                    if (mWifiConnectedKeys.add(key)) {
+                        sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+                        showAdbConnectedNotification(true);
+                    }
+                    break;
+                }
+                case MSG_WIFI_DEVICE_DISCONNECTED: {
+                    String key = (String) msg.obj;
+                    if (mWifiConnectedKeys.remove(key)) {
+                        sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+                        if (mWifiConnectedKeys.isEmpty()) {
+                            showAdbConnectedNotification(false);
+                        }
+                    }
+                    break;
+                }
+                case MSG_SERVER_CONNECTED: {
+                    int port = (int) msg.obj;
+                    onAdbdWifiServerConnected(port);
+                    synchronized (mAdbConnectionInfo) {
+                        mAdbConnectionInfo.setPort(port);
+                    }
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 1);
+                    break;
+                }
+                case MSG_SERVER_DISCONNECTED: {
+                    if (!mAdbWifiEnabled) {
+                        break;
+                    }
+                    int port = (int) msg.obj;
+                    onAdbdWifiServerDisconnected(port);
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 0);
+                    stopAdbDebuggingThread();
+                    if (mConnectionPortPoller != null) {
+                        mConnectionPortPoller.cancelAndWait();
+                        mConnectionPortPoller = null;
+                    }
+                    break;
+                }
             }
         }
 
@@ -534,6 +1247,142 @@
         private void cancelJobToUpdateAdbKeyStore() {
             removeMessages(AdbDebuggingHandler.MESSAGE_ADB_UPDATE_KEYSTORE);
         }
+
+        // Generates a random string of digits with size |size|.
+        private String createPairingCode(int size) {
+            String res = "";
+            SecureRandom rand = new SecureRandom();
+            for (int i = 0; i < size; ++i) {
+                res += rand.nextInt(10);
+            }
+
+            return res;
+        }
+
+        private void sendServerConnectionState(boolean connected, int port) {
+            Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+            intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, connected
+                    ? AdbManager.WIRELESS_STATUS_CONNECTED
+                    : AdbManager.WIRELESS_STATUS_DISCONNECTED);
+            intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
+
+        private void onAdbdWifiServerConnected(int port) {
+            // Send the paired devices list to the UI
+            sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+            sendServerConnectionState(true, port);
+        }
+
+        private void onAdbdWifiServerDisconnected(int port) {
+            // The TLS server disconnected while we had wireless debugging enabled.
+            // Let's disable it.
+            mWifiConnectedKeys.clear();
+            showAdbConnectedNotification(false);
+            sendServerConnectionState(false, port);
+        }
+
+        /**
+         * Returns the [bssid, ssid] of the current access point.
+         */
+        private AdbConnectionInfo getCurrentWifiApInfo() {
+            WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+            WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+            if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+                Slog.i(TAG, "Not connected to any wireless network. Not enabling adbwifi.");
+                return null;
+            }
+
+            String ssid = null;
+            if (wifiInfo.isPasspointAp() || wifiInfo.isOsuAp()) {
+                ssid = wifiInfo.getPasspointProviderFriendlyName();
+            } else {
+                ssid = wifiInfo.getSSID();
+                if (ssid == null || WifiSsid.NONE.equals(ssid)) {
+                    // OK, it's not in the connectionInfo; we have to go hunting for it
+                    List<WifiConfiguration> networks = wifiManager.getConfiguredNetworks();
+                    int length = networks.size();
+                    for (int i = 0; i < length; i++) {
+                        if (networks.get(i).networkId == wifiInfo.getNetworkId()) {
+                            ssid = networks.get(i).SSID;
+                        }
+                    }
+                    if (ssid == null) {
+                        Slog.e(TAG, "Unable to get ssid of the wifi AP.");
+                        return null;
+                    }
+                }
+            }
+
+            String bssid = wifiInfo.getBSSID();
+            if (bssid == null || bssid.isEmpty()) {
+                Slog.e(TAG, "Unable to get the wifi ap's BSSID.");
+                return null;
+            }
+            return new AdbConnectionInfo(bssid, ssid);
+        }
+
+        private boolean verifyWifiNetwork(String bssid, String ssid) {
+            // Check against a list of user-trusted networks.
+            if (mAdbKeyStore.isTrustedNetwork(bssid)) {
+                return true;
+            }
+
+            // Ask user to confirm using wireless debugging on this network.
+            startConfirmationForNetwork(ssid, bssid);
+            return false;
+        }
+
+        private void onPairingResult(String publicKey) {
+            if (publicKey == null) {
+                Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+                intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_FAIL);
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            } else {
+                Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+                intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+                        AdbManager.WIRELESS_STATUS_SUCCESS);
+                String fingerprints = getFingerprints(publicKey);
+                String hostname = "nouser@nohostname";
+                String[] args = publicKey.split("\\s+");
+                if (args.length > 1) {
+                    hostname = args[1];
+                }
+                PairDevice device = new PairDevice(fingerprints, hostname, false);
+                intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device);
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+                // Add the key into the keystore
+                mAdbKeyStore.setLastConnectionTime(publicKey,
+                        System.currentTimeMillis());
+                sendPersistKeyStoreMessage();
+                scheduleJobToUpdateAdbKeyStore();
+            }
+        }
+
+        private void sendPairingPortToUI(int port) {
+            Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+            intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+                    AdbManager.WIRELESS_STATUS_CONNECTED);
+            intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
+
+        private void sendPairedDevicesToUI(Map<String, PairDevice> devices) {
+            Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
+            // Map is not serializable, so need to downcast
+            intent.putExtra(AdbManager.WIRELESS_DEVICES_EXTRA, (HashMap) devices);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
+
+        private void updateUIPairCode(String code) {
+            if (DEBUG) Slog.i(TAG, "updateUIPairCode: " + code);
+
+            Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+            intent.putExtra(AdbManager.WIRELESS_PAIRING_CODE_EXTRA, code);
+            intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+                    AdbManager.WIRELESS_STATUS_PAIRING_CODE);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
     }
 
     private String getFingerprints(String key) {
@@ -570,7 +1419,34 @@
         return sb.toString();
     }
 
-    private void startConfirmation(String key, String fingerprints) {
+    private void startConfirmationForNetwork(String ssid, String bssid) {
+        List<Map.Entry<String, String>> extras = new ArrayList<Map.Entry<String, String>>();
+        extras.add(new AbstractMap.SimpleEntry<String, String>("ssid", ssid));
+        extras.add(new AbstractMap.SimpleEntry<String, String>("bssid", bssid));
+        int currentUserId = ActivityManager.getCurrentUser();
+        UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
+        String componentString;
+        if (userInfo.isAdmin()) {
+            componentString = Resources.getSystem().getString(
+                    com.android.internal.R.string.config_customAdbWifiNetworkConfirmationComponent);
+        } else {
+            componentString = Resources.getSystem().getString(
+                    com.android.internal.R.string.config_customAdbWifiNetworkConfirmationComponent);
+        }
+        ComponentName componentName = ComponentName.unflattenFromString(componentString);
+        if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras)
+                || startConfirmationService(componentName, userInfo.getUserHandle(),
+                        extras)) {
+            return;
+        }
+        Slog.e(TAG, "Unable to start customAdbWifiNetworkConfirmation[SecondaryUser]Component "
+                + componentString + " as an Activity or a Service");
+    }
+
+    private void startConfirmationForKey(String key, String fingerprints) {
+        List<Map.Entry<String, String>> extras = new ArrayList<Map.Entry<String, String>>();
+        extras.add(new AbstractMap.SimpleEntry<String, String>("key", key));
+        extras.add(new AbstractMap.SimpleEntry<String, String>("fingerprints", fingerprints));
         int currentUserId = ActivityManager.getCurrentUser();
         UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
         String componentString;
@@ -585,9 +1461,9 @@
                     R.string.config_customAdbPublicKeyConfirmationSecondaryUserComponent);
         }
         ComponentName componentName = ComponentName.unflattenFromString(componentString);
-        if (startConfirmationActivity(componentName, userInfo.getUserHandle(), key, fingerprints)
+        if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras)
                 || startConfirmationService(componentName, userInfo.getUserHandle(),
-                        key, fingerprints)) {
+                        extras)) {
             return;
         }
         Slog.e(TAG, "unable to start customAdbPublicKeyConfirmation[SecondaryUser]Component "
@@ -598,9 +1474,9 @@
      * @return true if the componentName led to an Activity that was started.
      */
     private boolean startConfirmationActivity(ComponentName componentName, UserHandle userHandle,
-            String key, String fingerprints) {
+            List<Map.Entry<String, String>> extras) {
         PackageManager packageManager = mContext.getPackageManager();
-        Intent intent = createConfirmationIntent(componentName, key, fingerprints);
+        Intent intent = createConfirmationIntent(componentName, extras);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
             try {
@@ -617,8 +1493,8 @@
      * @return true if the componentName led to a Service that was started.
      */
     private boolean startConfirmationService(ComponentName componentName, UserHandle userHandle,
-            String key, String fingerprints) {
-        Intent intent = createConfirmationIntent(componentName, key, fingerprints);
+            List<Map.Entry<String, String>> extras) {
+        Intent intent = createConfirmationIntent(componentName, extras);
         try {
             if (mContext.startServiceAsUser(intent, userHandle) != null) {
                 return true;
@@ -629,12 +1505,13 @@
         return false;
     }
 
-    private Intent createConfirmationIntent(ComponentName componentName, String key,
-            String fingerprints) {
+    private Intent createConfirmationIntent(ComponentName componentName,
+            List<Map.Entry<String, String>> extras) {
         Intent intent = new Intent();
         intent.setClassName(componentName.getPackageName(), componentName.getClassName());
-        intent.putExtra("key", key);
-        intent.putExtra("fingerprints", fingerprints);
+        for (Map.Entry<String, String> entry : extras) {
+            intent.putExtra(entry.getKey(), entry.getValue());
+        }
         return intent;
     }
 
@@ -717,13 +1594,22 @@
     }
 
     /**
-     * When {@code enabled} is {@code true}, this allows ADB debugging and starts the ADB hanler
-     * thread. When {@code enabled} is {@code false}, this disallows ADB debugging and shuts
-     * down the handler thread.
+     * When {@code enabled} is {@code true}, this allows ADB debugging and starts the ADB handler
+     * thread. When {@code enabled} is {@code false}, this disallows ADB debugging for the given
+     * @{code transportType}. See {@link IAdbTransport} for all available transport types.
+     * If all transport types are disabled, the ADB handler thread will shut down.
      */
-    public void setAdbEnabled(boolean enabled) {
-        mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MESSAGE_ADB_ENABLED
-                                          : AdbDebuggingHandler.MESSAGE_ADB_DISABLED);
+    public void setAdbEnabled(boolean enabled, byte transportType) {
+        if (transportType == AdbTransportType.USB) {
+            mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MESSAGE_ADB_ENABLED
+                                              : AdbDebuggingHandler.MESSAGE_ADB_DISABLED);
+        } else if (transportType == AdbTransportType.WIFI) {
+            mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MSG_ADBDWIFI_ENABLE
+                                              : AdbDebuggingHandler.MSG_ADBDWIFI_DISABLE);
+        } else {
+            throw new IllegalArgumentException(
+                    "setAdbEnabled called with unimplemented transport type=" + transportType);
+        }
     }
 
     /**
@@ -753,6 +1639,87 @@
     }
 
     /**
+     * Allows wireless debugging on the network identified by {@code bssid} either once
+     * or always if {@code alwaysAllow} is {@code true}.
+     */
+    public void allowWirelessDebugging(boolean alwaysAllow, String bssid) {
+        Message msg = mHandler.obtainMessage(AdbDebuggingHandler.MSG_ADBWIFI_ALLOW);
+        msg.arg1 = alwaysAllow ? 1 : 0;
+        msg.obj = bssid;
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * Denies wireless debugging connection on the last requested network.
+     */
+    public void denyWirelessDebugging() {
+        mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_ADBWIFI_DENY);
+    }
+
+    /**
+     * Returns the port adbwifi is currently opened on.
+     */
+    public int getAdbWirelessPort() {
+        AdbConnectionInfo info = getAdbConnectionInfo();
+        if (info == null) {
+            return 0;
+        }
+        return info.getPort();
+    }
+
+    /**
+     * Returns the list of paired devices.
+     */
+    public Map<String, PairDevice> getPairedDevices() {
+        AdbKeyStore keystore = new AdbKeyStore();
+        return keystore.getPairedDevices();
+    }
+
+    /**
+     * Unpair with device
+     */
+    public void unpairDevice(String fingerprint) {
+        Message message = Message.obtain(mHandler,
+                                         AdbDebuggingHandler.MSG_REQ_UNPAIR,
+                                         fingerprint);
+        mHandler.sendMessage(message);
+    }
+
+    /**
+     * Enable pairing by pairing code
+     */
+    public void enablePairingByPairingCode() {
+        mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_PAIR_PAIRING_CODE);
+    }
+
+    /**
+     * Enable pairing by pairing code
+     */
+    public void enablePairingByQrCode(String serviceName, String password) {
+        Bundle bundle = new Bundle();
+        bundle.putString("serviceName", serviceName);
+        bundle.putString("password", password);
+        Message message = Message.obtain(mHandler,
+                                         AdbDebuggingHandler.MSG_PAIR_QR_CODE,
+                                         bundle);
+        mHandler.sendMessage(message);
+    }
+
+    /**
+     * Disables pairing
+     */
+    public void disablePairing() {
+        mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_PAIRING_CANCEL);
+    }
+
+    /**
+     * Status enabled/disabled check
+     */
+    public boolean isAdbWifiEnabled() {
+        return mAdbWifiEnabled;
+    }
+
+    /**
      * Sends a message to the handler to persist the keystore.
      */
     private void sendPersistKeyStoreMessage() {
@@ -805,9 +1772,19 @@
         private File mKeyFile;
         private AtomicFile mAtomicKeyFile;
 
+        private List<String> mTrustedNetworks;
+        private static final int KEYSTORE_VERSION = 1;
+        private static final int MAX_SUPPORTED_KEYSTORE_VERSION = 1;
+        private static final String XML_KEYSTORE_START_TAG = "keyStore";
+        private static final String XML_ATTRIBUTE_VERSION = "version";
         private static final String XML_TAG_ADB_KEY = "adbKey";
         private static final String XML_ATTRIBUTE_KEY = "key";
         private static final String XML_ATTRIBUTE_LAST_CONNECTION = "lastConnection";
+        // A list of trusted networks a device can always wirelessly debug on (always allow).
+        // TODO: Move trusted networks list into a different file?
+        private static final String XML_TAG_WIFI_ACCESS_POINT = "wifiAP";
+        private static final String XML_ATTRIBUTE_WIFI_BSSID = "bssid";
+
         private static final String SYSTEM_KEY_FILE = "/adb_keys";
 
         /**
@@ -834,10 +1811,49 @@
         private void init() {
             initKeyFile();
             mKeyMap = getKeyMap();
+            mTrustedNetworks = getTrustedNetworks();
             mSystemKeys = getSystemKeysFromFile(SYSTEM_KEY_FILE);
             addUserKeysToKeyStore();
         }
 
+        public void addTrustedNetwork(String bssid) {
+            mTrustedNetworks.add(bssid);
+            sendPersistKeyStoreMessage();
+        }
+
+        public Map<String, PairDevice> getPairedDevices() {
+            Map<String, PairDevice> pairedDevices = new HashMap<String, PairDevice>();
+            for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
+                String fingerprints = getFingerprints(keyEntry.getKey());
+                String hostname = "nouser@nohostname";
+                String[] args = keyEntry.getKey().split("\\s+");
+                if (args.length > 1) {
+                    hostname = args[1];
+                }
+                pairedDevices.put(keyEntry.getKey(), new PairDevice(
+                        hostname, fingerprints, mWifiConnectedKeys.contains(keyEntry.getKey())));
+            }
+            return pairedDevices;
+        }
+
+        public String findKeyFromFingerprint(String fingerprint) {
+            for (Map.Entry<String, Long> entry : mKeyMap.entrySet()) {
+                String f = getFingerprints(entry.getKey());
+                if (fingerprint.equals(f)) {
+                    return entry.getKey();
+                }
+            }
+            return null;
+        }
+
+        public void removeKey(String key) {
+            if (mKeyMap.containsKey(key)) {
+                mKeyMap.remove(key);
+                writeKeys(mKeyMap.keySet());
+                sendPersistKeyStoreMessage();
+            }
+        }
+
         /**
          * Initializes the key file that will be used to persist the adb grants.
          */
@@ -907,6 +1923,78 @@
             try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
                 XmlPullParser parser = Xml.newPullParser();
                 parser.setInput(keyStream, StandardCharsets.UTF_8.name());
+                // Check for supported keystore version.
+                XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
+                if (parser.next() != XmlPullParser.END_DOCUMENT) {
+                    String tagName = parser.getName();
+                    if (tagName == null || !XML_KEYSTORE_START_TAG.equals(tagName)) {
+                        Slog.e(TAG, "Expected " + XML_KEYSTORE_START_TAG + ", but got tag="
+                                + tagName);
+                        return keyMap;
+                    }
+                    int keystoreVersion = Integer.parseInt(
+                            parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION));
+                    if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
+                        Slog.e(TAG, "Keystore version=" + keystoreVersion
+                                + " not supported (max_supported="
+                                + MAX_SUPPORTED_KEYSTORE_VERSION + ")");
+                        return keyMap;
+                    }
+                }
+                while (parser.next() != XmlPullParser.END_DOCUMENT) {
+                    String tagName = parser.getName();
+                    if (tagName == null) {
+                        break;
+                    } else if (!tagName.equals(XML_TAG_ADB_KEY)) {
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
+                    long connectionTime;
+                    try {
+                        connectionTime = Long.valueOf(
+                                parser.getAttributeValue(null, XML_ATTRIBUTE_LAST_CONNECTION));
+                    } catch (NumberFormatException e) {
+                        Slog.e(TAG,
+                                "Caught a NumberFormatException parsing the last connection time: "
+                                        + e);
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    keyMap.put(key, connectionTime);
+                }
+            } catch (IOException e) {
+                Slog.e(TAG, "Caught an IOException parsing the XML key file: ", e);
+            } catch (XmlPullParserException e) {
+                Slog.w(TAG, "Caught XmlPullParserException parsing the XML key file: ", e);
+                // The file could be written in a format prior to introducing keystore tag.
+                return getKeyMapBeforeKeystoreVersion();
+            }
+            return keyMap;
+        }
+
+
+        /**
+         * Returns the key map with the keys and last connection times from the key file.
+         * This implementation was prior to adding the XML_KEYSTORE_START_TAG.
+         */
+        private Map<String, Long> getKeyMapBeforeKeystoreVersion() {
+            Map<String, Long> keyMap = new HashMap<String, Long>();
+            // if the AtomicFile could not be instantiated before attempt again; if it still fails
+            // return an empty key map.
+            if (mAtomicKeyFile == null) {
+                initKeyFile();
+                if (mAtomicKeyFile == null) {
+                    Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
+                    return keyMap;
+                }
+            }
+            if (!mAtomicKeyFile.exists()) {
+                return keyMap;
+            }
+            try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(keyStream, StandardCharsets.UTF_8.name());
                 XmlUtils.beginDocument(parser, XML_TAG_ADB_KEY);
                 while (parser.next() != XmlPullParser.END_DOCUMENT) {
                     String tagName = parser.getName();
@@ -937,6 +2025,63 @@
         }
 
         /**
+         * Returns the map of trusted networks from the keystore file.
+         *
+         * This was implemented in keystore version 1.
+         */
+        private List<String> getTrustedNetworks() {
+            List<String> trustedNetworks = new ArrayList<String>();
+            // if the AtomicFile could not be instantiated before attempt again; if it still fails
+            // return an empty key map.
+            if (mAtomicKeyFile == null) {
+                initKeyFile();
+                if (mAtomicKeyFile == null) {
+                    Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
+                    return trustedNetworks;
+                }
+            }
+            if (!mAtomicKeyFile.exists()) {
+                return trustedNetworks;
+            }
+            try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(keyStream, StandardCharsets.UTF_8.name());
+                // Check for supported keystore version.
+                XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
+                if (parser.next() != XmlPullParser.END_DOCUMENT) {
+                    String tagName = parser.getName();
+                    if (tagName == null || !XML_KEYSTORE_START_TAG.equals(tagName)) {
+                        Slog.e(TAG, "Expected " + XML_KEYSTORE_START_TAG + ", but got tag="
+                                + tagName);
+                        return trustedNetworks;
+                    }
+                    int keystoreVersion = Integer.parseInt(
+                            parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION));
+                    if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
+                        Slog.e(TAG, "Keystore version=" + keystoreVersion
+                                + " not supported (max_supported="
+                                + MAX_SUPPORTED_KEYSTORE_VERSION);
+                        return trustedNetworks;
+                    }
+                }
+                while (parser.next() != XmlPullParser.END_DOCUMENT) {
+                    String tagName = parser.getName();
+                    if (tagName == null) {
+                        break;
+                    } else if (!tagName.equals(XML_TAG_WIFI_ACCESS_POINT)) {
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    String bssid = parser.getAttributeValue(null, XML_ATTRIBUTE_WIFI_BSSID);
+                    trustedNetworks.add(bssid);
+                }
+            } catch (IOException | XmlPullParserException | NumberFormatException e) {
+                Slog.e(TAG, "Caught an exception parsing the XML key file: ", e);
+            }
+            return trustedNetworks;
+        }
+
+        /**
          * Updates the keystore with keys that were previously set to be always allowed before the
          * connection time of keys was tracked.
          */
@@ -972,7 +2117,7 @@
             // if there is nothing in the key map then ensure any keys left in the keystore files
             // are deleted as well.
             filterOutOldKeys();
-            if (mKeyMap.isEmpty()) {
+            if (mKeyMap.isEmpty() && mTrustedNetworks.isEmpty()) {
                 deleteKeyStore();
                 return;
             }
@@ -990,6 +2135,8 @@
                 serializer.setOutput(keyStream, StandardCharsets.UTF_8.name());
                 serializer.startDocument(null, true);
 
+                serializer.startTag(null, XML_KEYSTORE_START_TAG);
+                serializer.attribute(null, XML_ATTRIBUTE_VERSION, String.valueOf(KEYSTORE_VERSION));
                 for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
                     serializer.startTag(null, XML_TAG_ADB_KEY);
                     serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey());
@@ -997,7 +2144,12 @@
                             String.valueOf(keyEntry.getValue()));
                     serializer.endTag(null, XML_TAG_ADB_KEY);
                 }
-
+                for (String bssid : mTrustedNetworks) {
+                    serializer.startTag(null, XML_TAG_WIFI_ACCESS_POINT);
+                    serializer.attribute(null, XML_ATTRIBUTE_WIFI_BSSID, bssid);
+                    serializer.endTag(null, XML_TAG_WIFI_ACCESS_POINT);
+                }
+                serializer.endTag(null, XML_KEYSTORE_START_TAG);
                 serializer.endDocument();
                 mAtomicKeyFile.finishWrite(keyStream);
             } catch (IOException e) {
@@ -1058,6 +2210,7 @@
          */
         public void deleteKeyStore() {
             mKeyMap.clear();
+            mTrustedNetworks.clear();
             deleteKeyFile();
             if (mAtomicKeyFile == null) {
                 return;
@@ -1139,5 +2292,14 @@
                 return false;
             }
         }
+
+        /**
+         * Returns whether the specified bssid is in the list of trusted networks. This requires
+         * that the user previously allowed wireless debugging on this network and selected the
+         * option to 'Always allow'.
+         */
+        public boolean isTrustedNetwork(String bssid) {
+            return mTrustedNetworks.contains(bssid);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index c125b1b..7aaf9be 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -15,21 +15,28 @@
  */
 package com.android.server.adb;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
+import android.debug.AdbManager;
 import android.debug.AdbManagerInternal;
+import android.debug.AdbTransportType;
 import android.debug.IAdbManager;
 import android.debug.IAdbTransport;
+import android.debug.PairDevice;
 import android.hardware.usb.UsbManager;
+import android.net.Uri;
 import android.os.Binder;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.adb.AdbServiceDumpProto;
 import android.sysprop.AdbProperties;
@@ -38,9 +45,9 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
-
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.dump.DualDumpOutputStream;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -50,6 +57,8 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * The Android Debug Bridge (ADB) service. This controls the availability of ADB and authorization
@@ -57,6 +66,27 @@
  */
 public class AdbService extends IAdbManager.Stub {
     /**
+     * Adb native daemon.
+     */
+    static final String ADBD = "adbd";
+
+    /**
+     * Command to start native service.
+     */
+    static final String CTL_START = "ctl.start";
+
+    /**
+     * Command to start native service.
+     */
+    static final String CTL_STOP = "ctl.stop";
+
+    // The tcp port adb is currently using
+    AtomicInteger mConnectionPort = new AtomicInteger(-1);
+
+    private final AdbConnectionPortListener mPortListener = new AdbConnectionPortListener();
+    private AdbDebuggingManager.AdbConnectionPortPoller mConnectionPortPoller;
+
+    /**
      * Manages the service lifecycle for {@code AdbService} in {@code SystemServer}.
      */
     public static class Lifecycle extends SystemService {
@@ -77,7 +107,8 @@
             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
                 mAdbService.systemReady();
             } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
-                mAdbService.bootCompleted();
+                FgThread.getHandler().sendMessage(obtainMessage(
+                        AdbService::bootCompleted, mAdbService));
             }
         }
     }
@@ -94,8 +125,14 @@
         }
 
         @Override
-        public boolean isAdbEnabled() {
-            return mAdbEnabled;
+        public boolean isAdbEnabled(byte transportType) {
+            if (transportType == AdbTransportType.USB) {
+                return mIsAdbUsbEnabled;
+            } else if (transportType == AdbTransportType.WIFI) {
+                return mIsAdbWifiEnabled;
+            }
+            throw new IllegalArgumentException(
+                    "isAdbEnabled called with unimplemented transport type=" + transportType);
         }
 
         @Override
@@ -107,93 +144,100 @@
         public File getAdbTempKeysFile() {
             return mDebuggingManager.getAdbTempKeysFile();
         }
-    }
 
-    private final class AdbHandler extends Handler {
-        AdbHandler(Looper looper) {
-            super(looper);
-            try {
-                /*
-                 * Use the normal bootmode persistent prop to maintain state of adb across
-                 * all boot modes.
-                 */
-                mAdbEnabled = containsFunction(
-                        SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, ""),
-                        UsbManager.USB_FUNCTION_ADB);
-
-                // register observer to listen for settings changes
-                mContentResolver.registerContentObserver(
-                        Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
-                        false, new AdbSettingsObserver());
-            } catch (Exception e) {
-                Slog.e(TAG, "Error initializing AdbHandler", e);
-            }
-        }
-
-        private boolean containsFunction(String functions, String function) {
-            int index = functions.indexOf(function);
-            if (index < 0) return false;
-            if (index > 0 && functions.charAt(index - 1) != ',') return false;
-            int charAfter = index + function.length();
-            if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
-            return true;
-        }
-
-        public void sendMessage(int what, boolean arg) {
-            removeMessages(what);
-            Message m = Message.obtain(this, what);
-            m.arg1 = (arg ? 1 : 0);
-            sendMessage(m);
+        @Override
+        public void startAdbdForTransport(byte transportType) {
+            FgThread.getHandler().sendMessage(obtainMessage(
+                    AdbService::setAdbdEnabledForTransport, AdbService.this, true, transportType));
         }
 
         @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_ENABLE_ADB:
-                    setAdbEnabled(msg.arg1 == 1);
-                    break;
-                case MSG_BOOT_COMPLETED:
-                    if (mDebuggingManager != null) {
-                        mDebuggingManager.setAdbEnabled(mAdbEnabled);
-                    }
-                    break;
-            }
+        public void stopAdbdForTransport(byte transportType) {
+            FgThread.getHandler().sendMessage(obtainMessage(
+                    AdbService::setAdbdEnabledForTransport, AdbService.this, false, transportType));
         }
     }
 
+    private void initAdbState() {
+        try {
+            /*
+             * Use the normal bootmode persistent prop to maintain state of adb across
+             * all boot modes.
+             */
+            mIsAdbUsbEnabled = containsFunction(
+                    SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, ""),
+                    UsbManager.USB_FUNCTION_ADB);
+            mIsAdbWifiEnabled = "1".equals(
+                    SystemProperties.get(WIFI_PERSISTENT_CONFIG_PROPERTY, "0"));
+
+            // register observer to listen for settings changes
+            mObserver = new AdbSettingsObserver();
+            mContentResolver.registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
+                    false, mObserver);
+            mContentResolver.registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED),
+                    false, mObserver);
+        } catch (Exception e) {
+            Slog.e(TAG, "Error in initAdbState", e);
+        }
+    }
+
+    private static boolean containsFunction(String functions, String function) {
+        int index = functions.indexOf(function);
+        if (index < 0) return false;
+        if (index > 0 && functions.charAt(index - 1) != ',') return false;
+        int charAfter = index + function.length();
+        if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
+        return true;
+    }
+
     private class AdbSettingsObserver extends ContentObserver {
+        private final Uri mAdbUsbUri = Settings.Global.getUriFor(Settings.Global.ADB_ENABLED);
+        private final Uri mAdbWifiUri = Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED);
+
         AdbSettingsObserver() {
             super(null);
         }
 
         @Override
-        public void onChange(boolean selfChange) {
-            boolean enable = (Settings.Global.getInt(mContentResolver,
-                    Settings.Global.ADB_ENABLED, 0) > 0);
-            mHandler.sendMessage(MSG_ENABLE_ADB, enable);
+        public void onChange(boolean selfChange, @NonNull Uri uri, @UserIdInt int userId) {
+            if (mAdbUsbUri.equals(uri)) {
+                boolean shouldEnable = (Settings.Global.getInt(mContentResolver,
+                        Settings.Global.ADB_ENABLED, 0) > 0);
+                FgThread.getHandler().sendMessage(obtainMessage(
+                        AdbService::setAdbEnabled, AdbService.this, shouldEnable,
+                            AdbTransportType.USB));
+            } else if (mAdbWifiUri.equals(uri)) {
+                boolean shouldEnable = (Settings.Global.getInt(mContentResolver,
+                        Settings.Global.ADB_WIFI_ENABLED, 0) > 0);
+                FgThread.getHandler().sendMessage(obtainMessage(
+                        AdbService::setAdbEnabled, AdbService.this, shouldEnable,
+                            AdbTransportType.WIFI));
+            }
         }
     }
 
     private static final String TAG = "AdbService";
     private static final boolean DEBUG = false;
 
-    private static final int MSG_ENABLE_ADB = 1;
-    private static final int MSG_BOOT_COMPLETED = 2;
-
     /**
      * The persistent property which stores whether adb is enabled or not.
      * May also contain vendor-specific default functions for testing purposes.
      */
     private static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
+    private static final String WIFI_PERSISTENT_CONFIG_PROPERTY = "persist.adb.tls_server.enable";
 
     private final Context mContext;
     private final ContentResolver mContentResolver;
-    private final AdbService.AdbHandler mHandler;
     private final ArrayMap<IBinder, IAdbTransport> mTransports = new ArrayMap<>();
 
-    private boolean mAdbEnabled;
+    private boolean mIsAdbUsbEnabled;
+    private boolean mIsAdbWifiEnabled;
     private AdbDebuggingManager mDebuggingManager;
 
+    private ContentObserver mObserver;
+
     private AdbService(Context context) {
         mContext = context;
         mContentResolver = context.getContentResolver();
@@ -204,8 +248,7 @@
             mDebuggingManager = new AdbDebuggingManager(context);
         }
 
-        mHandler = new AdbHandler(FgThread.get().getLooper());
-
+        initAdbState();
         LocalServices.addService(AdbManagerInternal.class, new AdbManagerInternalImpl());
     }
 
@@ -219,7 +262,9 @@
         // make sure the ADB_ENABLED setting value matches the current state
         try {
             Settings.Global.putInt(mContentResolver,
-                    Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+                    Settings.Global.ADB_ENABLED, mIsAdbUsbEnabled ? 1 : 0);
+            Settings.Global.putInt(mContentResolver,
+                    Settings.Global.ADB_WIFI_ENABLED, mIsAdbWifiEnabled ? 1 : 0);
         } catch (SecurityException e) {
             // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't be changed.
             Slog.d(TAG, "ADB_ENABLED is restricted.");
@@ -231,12 +276,16 @@
      */
     public void bootCompleted() {
         if (DEBUG) Slog.d(TAG, "boot completed");
-        mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.setAdbEnabled(mIsAdbUsbEnabled, AdbTransportType.USB);
+            mDebuggingManager.setAdbEnabled(mIsAdbWifiEnabled, AdbTransportType.WIFI);
+        }
     }
 
     @Override
-    public void allowDebugging(boolean alwaysAllow, String publicKey) {
+    public void allowDebugging(boolean alwaysAllow, @NonNull String publicKey) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        Preconditions.checkStringNotEmpty(publicKey);
         if (mDebuggingManager != null) {
             mDebuggingManager.allowDebugging(alwaysAllow, publicKey);
         }
@@ -285,24 +334,182 @@
                 PackageManager.FEATURE_CAMERA_ANY);
     }
 
-    private void setAdbEnabled(boolean enable) {
-        if (DEBUG) Slog.d(TAG, "setAdbEnabled(" + enable + "), mAdbEnabled=" + mAdbEnabled);
+    @Override
+    public void allowWirelessDebugging(boolean alwaysAllow, @NonNull String bssid) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        Preconditions.checkStringNotEmpty(bssid);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.allowWirelessDebugging(alwaysAllow, bssid);
+        }
+    }
 
-        if (enable == mAdbEnabled) {
+    @Override
+    public void denyWirelessDebugging() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.denyWirelessDebugging();
+        }
+    }
+
+    @Override
+    public Map<String, PairDevice> getPairedDevices() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        if (mDebuggingManager != null) {
+            return mDebuggingManager.getPairedDevices();
+        }
+        return null;
+    }
+
+    @Override
+    public void unpairDevice(@NonNull String fingerprint) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        Preconditions.checkStringNotEmpty(fingerprint);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.unpairDevice(fingerprint);
+        }
+    }
+
+    @Override
+    public void enablePairingByPairingCode() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.enablePairingByPairingCode();
+        }
+    }
+
+    @Override
+    public void enablePairingByQrCode(@NonNull String serviceName, @NonNull String password) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        Preconditions.checkStringNotEmpty(serviceName);
+        Preconditions.checkStringNotEmpty(password);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.enablePairingByQrCode(serviceName, password);
+        }
+    }
+
+    @Override
+    public void disablePairing() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.disablePairing();
+        }
+    }
+
+    @Override
+    public int getAdbWirelessPort() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        if (mDebuggingManager != null) {
+            return mDebuggingManager.getAdbWirelessPort();
+        }
+        // If ro.adb.secure=0
+        return mConnectionPort.get();
+    }
+
+    /**
+     * This listener is only used when ro.adb.secure=0. Otherwise, AdbDebuggingManager will
+     * do this.
+     */
+    class AdbConnectionPortListener implements AdbDebuggingManager.AdbConnectionPortListener {
+        public void onPortReceived(int port) {
+            if (port > 0 && port <= 65535) {
+                mConnectionPort.set(port);
+            } else {
+                mConnectionPort.set(-1);
+                // Turn off wifi debugging, since the server did not start.
+                try {
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 0);
+                } catch (SecurityException e) {
+                    // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't
+                    // be changed.
+                    Slog.d(TAG, "ADB_ENABLED is restricted.");
+                }
+            }
+            broadcastPortInfo(mConnectionPort.get());
+        }
+    }
+
+    private void broadcastPortInfo(int port) {
+        Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+        intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, (port >= 0)
+                ? AdbManager.WIRELESS_STATUS_CONNECTED
+                : AdbManager.WIRELESS_STATUS_DISCONNECTED);
+        intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        Slog.i(TAG, "sent port broadcast port=" + port);
+    }
+
+    private void startAdbd() {
+        SystemProperties.set(CTL_START, ADBD);
+    }
+
+    private void stopAdbd() {
+        if (!mIsAdbUsbEnabled && !mIsAdbWifiEnabled) {
+            SystemProperties.set(CTL_STOP, ADBD);
+        }
+    }
+
+    private void setAdbdEnabledForTransport(boolean enable, byte transportType) {
+        if (transportType == AdbTransportType.USB) {
+            mIsAdbUsbEnabled = enable;
+        } else if (transportType == AdbTransportType.WIFI) {
+            mIsAdbWifiEnabled = enable;
+        }
+        if (enable) {
+            startAdbd();
+        } else {
+            stopAdbd();
+        }
+    }
+
+    private void setAdbEnabled(boolean enable, byte transportType) {
+        if (DEBUG) {
+            Slog.d(TAG, "setAdbEnabled(" + enable + "), mIsAdbUsbEnabled=" + mIsAdbUsbEnabled
+                    + ", mIsAdbWifiEnabled=" + mIsAdbWifiEnabled + ", transportType="
+                        + transportType);
+        }
+
+        if (transportType == AdbTransportType.USB && enable != mIsAdbUsbEnabled) {
+            mIsAdbUsbEnabled = enable;
+        } else if (transportType == AdbTransportType.WIFI && enable != mIsAdbWifiEnabled) {
+            mIsAdbWifiEnabled = enable;
+            if (mIsAdbWifiEnabled) {
+                if (!AdbProperties.secure().orElse(false) && mDebuggingManager == null) {
+                    // Start adbd. If this is secure adb, then we defer enabling adb over WiFi.
+                    SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+                    mConnectionPortPoller =
+                            new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+                    mConnectionPortPoller.start();
+                }
+            } else {
+                // Stop adb over WiFi.
+                SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "0");
+                if (mConnectionPortPoller != null) {
+                    mConnectionPortPoller.cancelAndWait();
+                    mConnectionPortPoller = null;
+                }
+            }
+        } else {
+            // No change
             return;
         }
-        mAdbEnabled = enable;
+
+        if (enable) {
+            startAdbd();
+        } else {
+            stopAdbd();
+        }
 
         for (IAdbTransport transport : mTransports.values()) {
             try {
-                transport.onAdbEnabled(enable);
+                transport.onAdbEnabled(enable, transportType);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Unable to send onAdbEnabled to transport " + transport.toString());
             }
         }
 
         if (mDebuggingManager != null) {
-            mDebuggingManager.setAdbEnabled(enable);
+            mDebuggingManager.setAdbEnabled(enable, transportType);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2af04ae..53d2384 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -275,6 +275,7 @@
 import android.provider.DeviceConfig.Properties;
 import android.provider.Settings;
 import android.server.ServerProtoEnums;
+import android.sysprop.InitProperties;
 import android.sysprop.VoldProperties;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
@@ -346,6 +347,7 @@
 import com.android.server.SystemService;
 import com.android.server.SystemServiceManager;
 import com.android.server.ThreadPriorityBooster;
+import com.android.server.UserspaceRebootLogger;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
 import com.android.server.appop.AppOpsService;
@@ -2253,6 +2255,20 @@
         }
     }
 
+    private void maybeLogUserspaceRebootEvent() {
+        if (!UserspaceRebootLogger.shouldLogUserspaceRebootEvent()) {
+            return;
+        }
+        final int userId = mUserController.getCurrentUserId();
+        if (userId != UserHandle.USER_SYSTEM) {
+            // Only log for user0.
+            return;
+        }
+        // TODO(b/148767783): should we check all profiles under user0?
+        UserspaceRebootLogger.logEventAsync(StorageManager.isUserKeyUnlocked(userId),
+                BackgroundThread.getExecutor());
+    }
+
     /**
      * Encapsulates global settings related to hidden API enforcement behaviour, including tracking
      * the latest value via a content observer.
@@ -5306,6 +5322,12 @@
             // Start looking for apps that are abusing wake locks.
             Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
             mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
+            // Check if we are performing userspace reboot before setting sys.boot_completed to
+            // avoid race with init reseting sys.init.userspace_reboot.in_progress once sys
+            // .boot_completed is 1.
+            if (InitProperties.userspace_reboot_in_progress().orElse(false)) {
+                UserspaceRebootLogger.noteUserspaceRebootSuccess();
+            }
             // Tell anyone interested that we are done booting!
             SystemProperties.set("sys.boot_completed", "1");
 
@@ -5326,6 +5348,7 @@
                             }
                         }
                     });
+            maybeLogUserspaceRebootEvent();
             mUserController.scheduleStartProfiles();
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 972b106..af582c4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -36,6 +36,7 @@
 import android.app.IUidObserver;
 import android.app.KeyguardManager;
 import android.app.ProfilerInfo;
+import android.app.UserSwitchObserver;
 import android.app.WaitResult;
 import android.app.usage.AppStandbyInfo;
 import android.app.usage.ConfigurationStats;
@@ -1708,6 +1709,42 @@
         return 0;
     }
 
+    private boolean switchUserAndWaitForComplete(int userId) throws RemoteException {
+        UserInfo currentUser = mInterface.getCurrentUser();
+        if (currentUser != null && userId == currentUser.id) {
+            // Already switched to the correct user, exit early.
+            return true;
+        }
+
+        // Register switch observer.
+        final CountDownLatch switchLatch = new CountDownLatch(1);
+        mInterface.registerUserSwitchObserver(
+                new UserSwitchObserver() {
+                    @Override
+                    public void onUserSwitchComplete(int newUserId) {
+                        if (userId == newUserId) {
+                            switchLatch.countDown();
+                        }
+                    }
+                }, ActivityManagerShellCommand.class.getName());
+
+        // Switch.
+        boolean switched = mInterface.switchUser(userId);
+        if (!switched) {
+            // Switching failed, don't wait for the user switch observer.
+            return false;
+        }
+
+        // Wait.
+        try {
+            switched = switchLatch.await(USER_OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            getErrPrintWriter().println("Error: Thread interrupted unexpectedly.");
+        }
+
+        return switched;
+    }
+
     int runSwitchUser(PrintWriter pw) throws RemoteException {
         UserManager userManager = mInternal.mContext.getSystemService(UserManager.class);
         final int userSwitchable = userManager.getUserSwitchability();
@@ -1715,9 +1752,30 @@
             getErrPrintWriter().println("Error: " + userSwitchable);
             return -1;
         }
-        String user = getNextArgRequired();
-        mInterface.switchUser(Integer.parseInt(user));
-        return 0;
+        boolean wait = false;
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            if ("-w".equals(opt)) {
+                wait = true;
+            } else {
+                getErrPrintWriter().println("Error: unknown option: " + opt);
+                return -1;
+            }
+        }
+
+        int userId = Integer.parseInt(getNextArgRequired());
+        boolean switched;
+        if (wait) {
+            switched = switchUserAndWaitForComplete(userId);
+        } else {
+            switched = mInterface.switchUser(userId);
+        }
+        if (switched) {
+            return 0;
+        } else {
+            pw.printf("Error: Failed to switch to user %d\n", userId);
+            return 1;
+        }
     }
 
     int runGetCurrentUser(PrintWriter pw) throws RemoteException {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 8c60d0c..a19ac5e 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1133,7 +1133,7 @@
     @Override
     public void setBatteryState(final int status, final int health, final int plugType,
             final int level, final int temp, final int volt, final int chargeUAh,
-            final int chargeFullUAh) {
+            final int chargeFullUAh, final long chargeTimeToFullSeconds) {
         enforceCallingPermission();
 
         // BatteryService calls us here and we may update external state. It would be wrong
@@ -1145,7 +1145,7 @@
                     // The battery state has not changed, so we don't need to sync external
                     // stats immediately.
                     mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
-                            chargeUAh, chargeFullUAh);
+                            chargeUAh, chargeFullUAh, chargeTimeToFullSeconds);
                     return;
                 }
             }
@@ -1158,7 +1158,7 @@
             mWorker.scheduleRunnable(() -> {
                 synchronized (mStats) {
                     mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
-                            chargeUAh, chargeFullUAh);
+                            chargeUAh, chargeFullUAh, chargeTimeToFullSeconds);
                 }
             });
         });
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index c2652c0..8520cb7 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -51,6 +51,9 @@
 import android.app.AppGlobals;
 import android.app.AppProtoEnums;
 import android.app.IApplicationThread;
+import android.app.IUidObserver;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -103,6 +106,7 @@
 import com.android.server.wm.ActivityServiceConnectionsHolder;
 import com.android.server.wm.WindowManagerService;
 
+import dalvik.annotation.compat.VersionCodes;
 import dalvik.system.VMRuntime;
 
 import java.io.File;
@@ -280,6 +284,15 @@
     // lmkd reconnect delay in msecs
     private static final long LMKD_RECONNECT_DELAY_MS = 1000;
 
+    /**
+     * Native heap allocations will now have a non-zero tag in the most significant byte.
+     * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
+     * Pointers</a>
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = VersionCodes.Q)
+    private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
+
     ActivityManagerService mService = null;
 
     // To kill process groups asynchronously
@@ -1653,6 +1666,10 @@
                 runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
             }
 
+            if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
+                runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+            }
+
             String invokeWith = null;
             if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
                 // Debuggable apps may include a wrapper script with their library directory.
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index a5d9aa2..85b8ec6 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -234,8 +234,8 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        checkCompatChangeReadAndLogPermission();
         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
+        checkCompatChangeReadAndLogPermission();
         mCompatConfig.dumpConfig(pw);
     }
 
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 7f6dc55..3c21d1a 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -216,14 +216,14 @@
      * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
      * only applies to {@link VpnService} connections.
      */
-    private boolean mAlwaysOn = false;
+    @VisibleForTesting protected boolean mAlwaysOn = false;
 
     /**
      * Whether to disable traffic outside of this VPN even when the VPN is not connected. System
      * apps can still bypass by choosing explicit networks. Has no effect if {@link mAlwaysOn} is
-     * not set.
+     * not set. Applies to all types of VPNs.
      */
-    private boolean mLockdown = false;
+    @VisibleForTesting protected boolean mLockdown = false;
 
     /**
      * Set of packages in addition to the VPN app itself that can access the network directly when
@@ -252,14 +252,14 @@
     private final int mUserHandle;
 
     public Vpn(Looper looper, Context context, INetworkManagementService netService,
-            @UserIdInt int userHandle) {
-        this(looper, context, netService, userHandle,
+            @UserIdInt int userHandle, @NonNull KeyStore keyStore) {
+        this(looper, context, netService, userHandle, keyStore,
                 new SystemServices(context), new Ikev2SessionCreator());
     }
 
     @VisibleForTesting
     protected Vpn(Looper looper, Context context, INetworkManagementService netService,
-            int userHandle, SystemServices systemServices,
+            int userHandle, @NonNull KeyStore keyStore, SystemServices systemServices,
             Ikev2SessionCreator ikev2SessionCreator) {
         mContext = context;
         mNetd = netService;
@@ -285,7 +285,7 @@
         mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
         updateCapabilities(null /* defaultNetwork */);
 
-        loadAlwaysOnPackage();
+        loadAlwaysOnPackage(keyStore);
     }
 
     /**
@@ -437,23 +437,36 @@
     /**
      * Checks if a VPN app supports always-on mode.
      *
-     * In order to support the always-on feature, an app has to
+     * <p>In order to support the always-on feature, an app has to either have an installed
+     * PlatformVpnProfile, or:
+     *
      * <ul>
-     *     <li>target {@link VERSION_CODES#N API 24} or above, and
-     *     <li>not opt out through the {@link VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}
-     *         meta-data field.
+     *   <li>target {@link VERSION_CODES#N API 24} or above, and
+     *   <li>not opt out through the {@link VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}
+     *       meta-data field.
      * </ul>
      *
      * @param packageName the canonical package name of the VPN app
+     * @param keyStore the keystore instance to use for checking if the app has a Platform VPN
+     *     profile installed.
      * @return {@code true} if and only if the VPN app exists and supports always-on mode
      */
-    public boolean isAlwaysOnPackageSupported(String packageName) {
+    public boolean isAlwaysOnPackageSupported(String packageName, @NonNull KeyStore keyStore) {
         enforceSettingsPermission();
 
         if (packageName == null) {
             return false;
         }
 
+        final long oldId = Binder.clearCallingIdentity();
+        try {
+            if (getVpnProfilePrivileged(packageName, keyStore) != null) {
+                return true;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+
         PackageManager pm = mContext.getPackageManager();
         ApplicationInfo appInfo = null;
         try {
@@ -485,27 +498,31 @@
     }
 
     /**
-     * Configures an always-on VPN connection through a specific application.
-     * This connection is automatically granted and persisted after a reboot.
+     * Configures an always-on VPN connection through a specific application. This connection is
+     * automatically granted and persisted after a reboot.
      *
-     * <p>The designated package should exist and declare a {@link VpnService} in its
-     *    manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
-     *    otherwise the call will fail.
+     * <p>The designated package should either have a PlatformVpnProfile installed, or declare a
+     * {@link VpnService} in its manifest guarded by {@link
+     * android.Manifest.permission.BIND_VPN_SERVICE}, otherwise the call will fail.
      *
      * <p>Note that this method does not check if the VPN app supports always-on mode. The check is
-     *    delayed to {@link #startAlwaysOnVpn()}, which is always called immediately after this
-     *    method in {@link android.net.IConnectivityManager#setAlwaysOnVpnPackage}.
+     * delayed to {@link #startAlwaysOnVpn()}, which is always called immediately after this method
+     * in {@link android.net.IConnectivityManager#setAlwaysOnVpnPackage}.
      *
      * @param packageName the package to designate as always-on VPN supplier.
      * @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
      * @param lockdownWhitelist packages to be whitelisted from lockdown.
+     * @param keyStore the Keystore instance to use for checking of PlatformVpnProfile(s)
      * @return {@code true} if the package has been set as always-on, {@code false} otherwise.
      */
     public synchronized boolean setAlwaysOnPackage(
-            String packageName, boolean lockdown, List<String> lockdownWhitelist) {
+            @Nullable String packageName,
+            boolean lockdown,
+            @Nullable List<String> lockdownWhitelist,
+            @NonNull KeyStore keyStore) {
         enforceControlPermissionOrInternalCaller();
 
-        if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownWhitelist)) {
+        if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownWhitelist, keyStore)) {
             saveAlwaysOnPackage();
             return true;
         }
@@ -513,20 +530,22 @@
     }
 
     /**
-     * Configures an always-on VPN connection through a specific application, the same as
-     * {@link #setAlwaysOnPackage}.
+     * Configures an always-on VPN connection through a specific application, the same as {@link
+     * #setAlwaysOnPackage}.
      *
-     * Does not perform permission checks. Does not persist any of the changes to storage.
+     * <p>Does not perform permission checks. Does not persist any of the changes to storage.
      *
      * @param packageName the package to designate as always-on VPN supplier.
      * @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
      * @param lockdownWhitelist packages to be whitelisted from lockdown. This is only used if
-     *        {@code lockdown} is {@code true}. Packages must not contain commas.
+     *     {@code lockdown} is {@code true}. Packages must not contain commas.
+     * @param keyStore the system keystore instance to check for profiles
      * @return {@code true} if the package has been set as always-on, {@code false} otherwise.
      */
     @GuardedBy("this")
     private boolean setAlwaysOnPackageInternal(
-            String packageName, boolean lockdown, List<String> lockdownWhitelist) {
+            @Nullable String packageName, boolean lockdown,
+            @Nullable List<String> lockdownWhitelist, @NonNull KeyStore keyStore) {
         if (VpnConfig.LEGACY_VPN.equals(packageName)) {
             Log.w(TAG, "Not setting legacy VPN \"" + packageName + "\" as always-on.");
             return false;
@@ -542,11 +561,18 @@
         }
 
         if (packageName != null) {
-            // TODO: Give the minimum permission possible; if there is a Platform VPN profile, only
-            // grant ACTIVATE_PLATFORM_VPN.
-            // Pre-authorize new always-on VPN package. Grant the full ACTIVATE_VPN appop, allowing
-            // both VpnService and Platform VPNs.
-            if (!setPackageAuthorization(packageName, VpnManager.TYPE_VPN_SERVICE)) {
+            final VpnProfile profile;
+            final long oldId = Binder.clearCallingIdentity();
+            try {
+                profile = getVpnProfilePrivileged(packageName, keyStore);
+            } finally {
+                Binder.restoreCallingIdentity(oldId);
+            }
+
+            // Pre-authorize new always-on VPN package.
+            final int grantType =
+                    (profile == null) ? VpnManager.TYPE_VPN_SERVICE : VpnManager.TYPE_VPN_PLATFORM;
+            if (!setPackageAuthorization(packageName, grantType)) {
                 return false;
             }
             mAlwaysOn = true;
@@ -611,11 +637,9 @@
         }
     }
 
-    /**
-     * Load the always-on package and lockdown config from Settings.Secure
-     */
+    /** Load the always-on package and lockdown config from Settings. */
     @GuardedBy("this")
-    private void loadAlwaysOnPackage() {
+    private void loadAlwaysOnPackage(@NonNull KeyStore keyStore) {
         final long token = Binder.clearCallingIdentity();
         try {
             final String alwaysOnPackage = mSystemServices.settingsSecureGetStringForUser(
@@ -626,17 +650,21 @@
                     Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST, mUserHandle);
             final List<String> whitelistedPackages = TextUtils.isEmpty(whitelistString)
                     ? Collections.emptyList() : Arrays.asList(whitelistString.split(","));
-            setAlwaysOnPackageInternal(alwaysOnPackage, alwaysOnLockdown, whitelistedPackages);
+            setAlwaysOnPackageInternal(
+                    alwaysOnPackage, alwaysOnLockdown, whitelistedPackages, keyStore);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
     /**
+     * Starts the currently selected always-on VPN
+     *
+     * @param keyStore the keyStore instance for looking up PlatformVpnProfile(s)
      * @return {@code true} if the service was started, the service was already connected, or there
-     *         was no always-on VPN to start. {@code false} otherwise.
+     *     was no always-on VPN to start. {@code false} otherwise.
      */
-    public boolean startAlwaysOnVpn() {
+    public boolean startAlwaysOnVpn(@NonNull KeyStore keyStore) {
         final String alwaysOnPackage;
         synchronized (this) {
             alwaysOnPackage = getAlwaysOnPackage();
@@ -645,8 +673,8 @@
                 return true;
             }
             // Remove always-on VPN if it's not supported.
-            if (!isAlwaysOnPackageSupported(alwaysOnPackage)) {
-                setAlwaysOnPackage(null, false, null);
+            if (!isAlwaysOnPackageSupported(alwaysOnPackage, keyStore)) {
+                setAlwaysOnPackage(null, false, null, keyStore);
                 return false;
             }
             // Skip if the service is already established. This isn't bulletproof: it's not bound
@@ -657,10 +685,25 @@
             }
         }
 
-        // Tell the OS that background services in this app need to be allowed for
-        // a short time, so we can bootstrap the VPN service.
         final long oldId = Binder.clearCallingIdentity();
         try {
+            // Prefer VPN profiles, if any exist.
+            VpnProfile profile = getVpnProfilePrivileged(alwaysOnPackage, keyStore);
+            if (profile != null) {
+                startVpnProfilePrivileged(profile, alwaysOnPackage,
+                        null /* keyStore for private key retrieval - unneeded */);
+
+                // If the above startVpnProfilePrivileged() call returns, the Ikev2VpnProfile was
+                // correctly parsed, and the VPN has started running in a different thread. The only
+                // other possibility is that the above call threw an exception, which will be
+                // caught below, and returns false (clearing the always-on VPN). Once started, the
+                // Platform VPN cannot permanently fail, and is resilient to temporary failures. It
+                // will continue retrying until shut down by the user, or always-on is toggled off.
+                return true;
+            }
+
+            // Tell the OS that background services in this app need to be allowed for
+            // a short time, so we can bootstrap the VPN service.
             DeviceIdleController.LocalService idleController =
                     LocalServices.getService(DeviceIdleController.LocalService.class);
             idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage,
@@ -675,6 +718,9 @@
                 Log.e(TAG, "VpnService " + serviceIntent + " failed to start", e);
                 return false;
             }
+        } catch (Exception e) {
+            Log.e(TAG, "Error starting always-on VPN", e);
+            return false;
         } finally {
             Binder.restoreCallingIdentity(oldId);
         }
@@ -773,6 +819,7 @@
     }
 
     /** Prepare the VPN for the given package. Does not perform permission checks. */
+    @GuardedBy("this")
     private void prepareInternal(String newPackage) {
         long token = Binder.clearCallingIdentity();
         try {
@@ -794,10 +841,10 @@
                     // ignore
                 }
                 mContext.unbindService(mConnection);
-                mConnection = null;
+                cleanupVpnStateLocked();
             } else if (mVpnRunner != null) {
+                // cleanupVpnStateLocked() is called from mVpnRunner.exit()
                 mVpnRunner.exit();
-                mVpnRunner = null;
             }
 
             try {
@@ -1104,7 +1151,6 @@
      */
     public synchronized ParcelFileDescriptor establish(VpnConfig config) {
         // Check if the caller is already prepared.
-        UserManager mgr = UserManager.get(mContext);
         if (Binder.getCallingUid() != mOwnerUID) {
             return null;
         }
@@ -1118,10 +1164,7 @@
         long token = Binder.clearCallingIdentity();
         try {
             // Restricted users are not allowed to create VPNs, they are tied to Owner
-            UserInfo user = mgr.getUserInfo(mUserHandle);
-            if (user.isRestricted()) {
-                throw new SecurityException("Restricted users cannot establish VPNs");
-            }
+            enforceNotRestrictedUser();
 
             ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
                     null, 0, mUserHandle);
@@ -1543,24 +1586,30 @@
         public void interfaceRemoved(String interfaze) {
             synchronized (Vpn.this) {
                 if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
-                    mStatusIntent = null;
-                    mNetworkCapabilities.setUids(null);
-                    mConfig = null;
-                    mInterface = null;
                     if (mConnection != null) {
                         mContext.unbindService(mConnection);
-                        mConnection = null;
-                        agentDisconnect();
+                        cleanupVpnStateLocked();
                     } else if (mVpnRunner != null) {
-                        // agentDisconnect must be called from mVpnRunner.exit()
+                        // cleanupVpnStateLocked() is called from mVpnRunner.exit()
                         mVpnRunner.exit();
-                        mVpnRunner = null;
                     }
                 }
             }
         }
     };
 
+    private void cleanupVpnStateLocked() {
+        mStatusIntent = null;
+        mNetworkCapabilities.setUids(null);
+        mConfig = null;
+        mInterface = null;
+
+        // Unconditionally clear both VpnService and VpnRunner fields.
+        mVpnRunner = null;
+        mConnection = null;
+        agentDisconnect();
+    }
+
     private void enforceControlPermission() {
         mContext.enforceCallingPermission(Manifest.permission.CONTROL_VPN, "Unauthorized Caller");
     }
@@ -1673,6 +1722,25 @@
     }
 
     /**
+     * Gets the currently running App-based VPN type
+     *
+     * @return the {@link VpnManager.VpnType}. {@link VpnManager.TYPE_VPN_NONE} if not running an
+     *     app-based VPN. While VpnService-based VPNs are always app VPNs and LegacyVpn is always
+     *     Settings-based, the Platform VPNs can be initiated by both apps and Settings.
+     */
+    public synchronized int getActiveAppVpnType() {
+        if (VpnConfig.LEGACY_VPN.equals(mPackage)) {
+            return VpnManager.TYPE_VPN_NONE;
+        }
+
+        if (mVpnRunner != null && mVpnRunner instanceof IkeV2VpnRunner) {
+            return VpnManager.TYPE_VPN_PLATFORM;
+        } else {
+            return VpnManager.TYPE_VPN_SERVICE;
+        }
+    }
+
+    /**
      * @param uid The target uid.
      *
      * @return {@code true} if {@code uid} is included in one of the mBlockedUidsAsToldToNetd
@@ -1800,6 +1868,17 @@
         throw new IllegalStateException("Unable to find IPv4 default gateway");
     }
 
+    private void enforceNotRestrictedUser() {
+        Binder.withCleanCallingIdentity(() -> {
+            final UserManager mgr = UserManager.get(mContext);
+            final UserInfo user = mgr.getUserInfo(mUserHandle);
+
+            if (user.isRestricted()) {
+                throw new SecurityException("Restricted users cannot configure VPNs");
+            }
+        });
+    }
+
     /**
      * Start legacy VPN, controlling native daemons as needed. Creates a
      * secondary thread to perform connection work, returning quickly.
@@ -1862,6 +1941,27 @@
         // Prepare arguments for racoon.
         String[] racoon = null;
         switch (profile.type) {
+            case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+                // Secret key is still just the alias (not the actual private key). The private key
+                // is retrieved from the KeyStore during conversion of the VpnProfile to an
+                // Ikev2VpnProfile.
+                profile.ipsecSecret = Ikev2VpnProfile.PREFIX_KEYSTORE_ALIAS + privateKey;
+                profile.ipsecUserCert = userCert;
+                // Fallthrough
+            case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
+                profile.ipsecCaCert = caCert;
+
+                // Start VPN profile
+                startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore);
+                return;
+            case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+                // Ikev2VpnProfiles expect a base64-encoded preshared key.
+                profile.ipsecSecret =
+                        Ikev2VpnProfile.encodeForIpsecSecret(profile.ipsecSecret.getBytes());
+
+                // Start VPN profile
+                startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore);
+                return;
             case VpnProfile.TYPE_L2TP_IPSEC_PSK:
                 racoon = new String[] {
                     iface, profile.server, "udppsk", profile.ipsecIdentifier,
@@ -2020,7 +2120,25 @@
 
         public abstract void run();
 
-        protected abstract void exit();
+        /**
+         * Disconnects the NetworkAgent and cleans up all state related to the VpnRunner.
+         *
+         * <p>All outer Vpn instance state is cleaned up in cleanupVpnStateLocked()
+         */
+        protected abstract void exitVpnRunner();
+
+        /**
+         * Triggers the cleanup of the VpnRunner, and additionally cleans up Vpn instance-wide state
+         *
+         * <p>This method ensures that simple calls to exit() will always clean up global state
+         * properly.
+         */
+        protected final void exit() {
+            synchronized (Vpn.this) {
+                exitVpnRunner();
+                cleanupVpnStateLocked();
+            }
+        }
     }
 
     interface IkeV2VpnRunnerCallback {
@@ -2350,17 +2468,6 @@
         }
 
         /**
-         * Triggers cleanup of outer class' state
-         *
-         * <p>Can be called from any thread, as it does not mutate state in the Ikev2VpnRunner.
-         */
-        private void cleanupVpnState() {
-            synchronized (Vpn.this) {
-                agentDisconnect();
-            }
-        }
-
-        /**
          * Cleans up all Ikev2VpnRunner internal state
          *
          * <p>This method MUST always be called on the mExecutor thread in order to ensure
@@ -2379,10 +2486,7 @@
         }
 
         @Override
-        public void exit() {
-            // Cleanup outer class' state immediately, otherwise race conditions may ensue.
-            cleanupVpnState();
-
+        public void exitVpnRunner() {
             mExecutor.execute(() -> {
                 shutdownVpnRunner();
             });
@@ -2481,10 +2585,9 @@
 
         /** Tears down this LegacyVpn connection */
         @Override
-        public void exit() {
+        public void exitVpnRunner() {
             // We assume that everything is reset after stopping the daemons.
             interrupt();
-            agentDisconnect();
             try {
                 mContext.unregisterReceiver(mBroadcastReceiver);
             } catch (IllegalArgumentException e) {}
@@ -2757,6 +2860,7 @@
         checkNotNull(keyStore, "KeyStore missing");
 
         verifyCallingUidAndPackage(packageName);
+        enforceNotRestrictedUser();
 
         final byte[] encodedProfile = profile.encode();
         if (encodedProfile.length > MAX_VPN_PROFILE_SIZE_BYTES) {
@@ -2780,6 +2884,10 @@
         return isVpnProfilePreConsented(mContext, packageName);
     }
 
+    private boolean isCurrentIkev2VpnLocked(@NonNull String packageName) {
+        return isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner;
+    }
+
     /**
      * Deletes an app-provisioned VPN profile.
      *
@@ -2792,9 +2900,21 @@
         checkNotNull(keyStore, "KeyStore missing");
 
         verifyCallingUidAndPackage(packageName);
+        enforceNotRestrictedUser();
 
         Binder.withCleanCallingIdentity(
                 () -> {
+                    // If this profile is providing the current VPN, turn it off, disabling
+                    // always-on as well if enabled.
+                    if (isCurrentIkev2VpnLocked(packageName)) {
+                        if (mAlwaysOn) {
+                            // Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
+                            setAlwaysOnPackage(null, false, null, keyStore);
+                        } else {
+                            prepareInternal(VpnConfig.LEGACY_VPN);
+                        }
+                    }
+
                     keyStore.delete(getProfileNameForPackage(packageName), Process.SYSTEM_UID);
                 });
     }
@@ -2834,6 +2954,8 @@
         checkNotNull(packageName, "No package name provided");
         checkNotNull(keyStore, "KeyStore missing");
 
+        enforceNotRestrictedUser();
+
         // Prepare VPN for startup
         if (!prepare(packageName, null /* newPackage */, VpnManager.TYPE_VPN_PLATFORM)) {
             throw new SecurityException("User consent not granted for package " + packageName);
@@ -2846,24 +2968,35 @@
                         throw new IllegalArgumentException("No profile found for " + packageName);
                     }
 
-                    startVpnProfilePrivileged(profile, packageName);
+                    startVpnProfilePrivileged(profile, packageName,
+                            null /* keyStore for private key retrieval - unneeded */);
                 });
     }
 
-    private void startVpnProfilePrivileged(
-            @NonNull VpnProfile profile, @NonNull String packageName) {
-        // Ensure that no other previous instance is running.
-        if (mVpnRunner != null) {
-            mVpnRunner.exit();
-            mVpnRunner = null;
-        }
+    private synchronized void startVpnProfilePrivileged(
+            @NonNull VpnProfile profile, @NonNull String packageName, @Nullable KeyStore keyStore) {
+        // Make sure VPN is prepared. This method can be called by user apps via startVpnProfile(),
+        // by the Setting app via startLegacyVpn(), or by ConnectivityService via
+        // startAlwaysOnVpn(), so this is the common place to prepare the VPN. This also has the
+        // nice property of ensuring there are no other VpnRunner instances running.
+        prepareInternal(packageName);
         updateState(DetailedState.CONNECTING, "startPlatformVpn");
 
         try {
             // Build basic config
             mConfig = new VpnConfig();
-            mConfig.user = packageName;
-            mConfig.isMetered = profile.isMetered;
+            if (VpnConfig.LEGACY_VPN.equals(packageName)) {
+                mConfig.legacy = true;
+                mConfig.session = profile.name;
+                mConfig.user = profile.key;
+
+                // TODO: Add support for configuring meteredness via Settings. Until then, use a
+                // safe default.
+                mConfig.isMetered = true;
+            } else {
+                mConfig.user = packageName;
+                mConfig.isMetered = profile.isMetered;
+            }
             mConfig.startTime = SystemClock.elapsedRealtime();
             mConfig.proxyInfo = profile.proxy;
 
@@ -2871,7 +3004,8 @@
                 case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
                 case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
                 case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
-                    mVpnRunner = new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile));
+                    mVpnRunner =
+                            new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile, keyStore));
                     mVpnRunner.start();
                     break;
                 default:
@@ -2899,13 +3033,13 @@
     public synchronized void stopVpnProfile(@NonNull String packageName) {
         checkNotNull(packageName, "No package name provided");
 
+        enforceNotRestrictedUser();
+
         // To stop the VPN profile, the caller must be the current prepared package and must be
         // running an Ikev2VpnProfile.
-        if (!isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner) {
-            return;
+        if (isCurrentIkev2VpnLocked(packageName)) {
+            prepareInternal(VpnConfig.LEGACY_VPN);
         }
-
-        prepareInternal(VpnConfig.LEGACY_VPN);
     }
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 9a85f952..a122611 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1092,10 +1092,11 @@
         HdmiDeviceInfo avr = getAvrDeviceInfo();
         if (avr != null
                 && (avrAddress == avr.getLogicalAddress())
-                && isConnectedToArcPort(avr.getPhysicalAddress())
-                && isDirectConnectAddress(avr.getPhysicalAddress())) {
+                && isConnectedToArcPort(avr.getPhysicalAddress())) {
             if (enabled) {
-                return isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId());
+                return isConnected(avr.getPortId())
+                    && isArcFeatureEnabled(avr.getPortId())
+                    && isDirectConnectAddress(avr.getPhysicalAddress());
             } else {
                 return true;
             }
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index e655b35..6fa9923 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -1502,11 +1502,11 @@
                     pw.println();
                 }
             }
-            if (job.getExtras() != null && !job.getExtras().maybeIsEmpty()) {
+            if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
                 pw.print(prefix); pw.print("  Extras: ");
                 pw.println(job.getExtras().toShortString());
             }
-            if (job.getTransientExtras() != null && !job.getTransientExtras().maybeIsEmpty()) {
+            if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
                 pw.print(prefix); pw.print("  Transient extras: ");
                 pw.println(job.getTransientExtras().toShortString());
             }
@@ -1702,10 +1702,10 @@
                             job.getTriggerContentMaxDelay());
                 }
             }
-            if (job.getExtras() != null && !job.getExtras().maybeIsEmpty()) {
+            if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
                 job.getExtras().writeToProto(proto, JobStatusDumpProto.JobInfo.EXTRAS);
             }
-            if (job.getTransientExtras() != null && !job.getTransientExtras().maybeIsEmpty()) {
+            if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
                 job.getTransientExtras().writeToProto(proto, JobStatusDumpProto.JobInfo.TRANSIENT_EXTRAS);
             }
             if (job.getClipData() != null) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index a7f8618..896bf67 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1253,7 +1253,7 @@
         final NetworkStats uidSnapshot = getNetworkStatsUidDetail(INTERFACES_ALL);
         Trace.traceEnd(TRACE_TAG_NETWORK);
         Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotXt");
-        final NetworkStats xtSnapshot = getNetworkStatsXt();
+        final NetworkStats xtSnapshot = readNetworkStatsSummaryXt();
         Trace.traceEnd(TRACE_TAG_NETWORK);
         Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotDev");
         final NetworkStats devSnapshot = readNetworkStatsSummaryDev();
@@ -1742,18 +1742,6 @@
                 mUseBpfTrafficStats);
         uidSnapshot.combineAllValues(tetherSnapshot);
 
-        final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
-                Context.TELEPHONY_SERVICE);
-
-        // fold video calling data usage stats into uid snapshot
-        final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
-        if (vtStats != null) {
-            vtStats.filter(UID_ALL, ifaces, TAG_ALL);
-            mStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats,
-                    mUseBpfTrafficStats);
-            uidSnapshot.combineAllValues(vtStats);
-        }
-
         // get a stale copy of uid stats snapshot provided by providers.
         final NetworkStats providerStats = getNetworkStatsFromProviders(STATS_PER_UID);
         providerStats.filter(UID_ALL, ifaces, TAG_ALL);
@@ -1766,24 +1754,6 @@
     }
 
     /**
-     * Return snapshot of current XT statistics with video calling data usage statistics.
-     */
-    private NetworkStats getNetworkStatsXt() throws RemoteException {
-        final NetworkStats xtSnapshot = readNetworkStatsSummaryXt();
-
-        final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
-                Context.TELEPHONY_SERVICE);
-
-        // Merge video calling data usage into XT
-        final NetworkStats vtSnapshot = telephonyManager.getVtDataUsage(STATS_PER_IFACE);
-        if (vtSnapshot != null) {
-            xtSnapshot.combineAllValues(vtSnapshot);
-        }
-
-        return xtSnapshot;
-    }
-
-    /**
      * Return snapshot of current tethering statistics. Will return empty
      * {@link NetworkStats} if any problems are encountered.
      */
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d8f5dfb..709811e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9764,8 +9764,8 @@
     }
 
     @Override
-    public void notifyDexLoad(String loadingPackageName, List<String> classLoaderNames,
-            List<String> classPaths, String loaderIsa) {
+    public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap,
+            String loaderIsa) {
         int userId = UserHandle.getCallingUserId();
         ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
         if (ai == null) {
@@ -9773,7 +9773,7 @@
                 + loadingPackageName + ", user=" + userId);
             return;
         }
-        mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId);
+        mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 3be51c5..98c6c6d 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -562,6 +562,9 @@
                             android.provider.Settings.Global.putStringForUser(cr,
                                     android.provider.Settings.Global.ADB_ENABLED, "0",
                                     userId);
+                            android.provider.Settings.Global.putStringForUser(cr,
+                                    android.provider.Settings.Global.ADB_WIFI_ENABLED, "0",
+                                    userId);
                         }
                     }
                     break;
@@ -702,6 +705,7 @@
                 break;
 
             case android.provider.Settings.Global.ADB_ENABLED:
+            case android.provider.Settings.Global.ADB_WIFI_ENABLED:
                 if ("0".equals(value)) {
                     return false;
                 }
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 9e86a4b..441fa75 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -44,6 +44,8 @@
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.PackageManagerServiceUtils;
 
+import dalvik.system.VMRuntime;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.Arrays;
@@ -143,22 +145,15 @@
      * return as fast as possible.
      *
      * @param loadingAppInfo the package performing the load
-     * @param classLoadersNames the names of the class loaders present in the loading chain. The
-     *    list encodes the class loader chain in the natural order. The first class loader has
-     *    the second one as its parent and so on. The dex files present in the class path of the
-     *    first class loader will be recorded in the usage file.
-     * @param classPaths the class paths corresponding to the class loaders names from
-     *     {@param classLoadersNames}. The the first element corresponds to the first class loader
-     *     and so on. A classpath is represented as a list of dex files separated by
-     *     {@code File.pathSeparator}, or null if the class loader's classpath is not known.
-     *     The dex files found in the first class path will be recorded in the usage file.
+     * @param classLoaderContextMap a map from file paths to dex files that have been loaded to
+     *     the class loader context that was used to load them.
      * @param loaderIsa the ISA of the app loading the dex files
      * @param loaderUserId the user id which runs the code loading the dex files
      */
-    public void notifyDexLoad(ApplicationInfo loadingAppInfo, List<String> classLoadersNames,
-            List<String> classPaths, String loaderIsa, int loaderUserId) {
+    public void notifyDexLoad(ApplicationInfo loadingAppInfo,
+            Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId) {
         try {
-            notifyDexLoadInternal(loadingAppInfo, classLoadersNames, classPaths, loaderIsa,
+            notifyDexLoadInternal(loadingAppInfo, classLoaderContextMap, loaderIsa,
                     loaderUserId);
         } catch (Exception e) {
             Slog.w(TAG, "Exception while notifying dex load for package " +
@@ -168,46 +163,23 @@
 
     @VisibleForTesting
     /*package*/ void notifyDexLoadInternal(ApplicationInfo loadingAppInfo,
-            List<String> classLoaderNames, List<String> classPaths, String loaderIsa,
+            Map<String, String> classLoaderContextMap, String loaderIsa,
             int loaderUserId) {
-        if (classLoaderNames.size() != classPaths.size()) {
-            Slog.wtf(TAG, "Bad call to noitfyDexLoad: args have different size");
+        if (classLoaderContextMap == null) {
             return;
         }
-        if (classLoaderNames.isEmpty()) {
+        if (classLoaderContextMap.isEmpty()) {
             Slog.wtf(TAG, "Bad call to notifyDexLoad: class loaders list is empty");
             return;
         }
         if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
-            Slog.w(TAG, "Loading dex files " + classPaths + " in unsupported ISA: " +
-                    loaderIsa + "?");
+            Slog.w(TAG, "Loading dex files " + classLoaderContextMap.keySet()
+                    + " in unsupported ISA: " + loaderIsa + "?");
             return;
         }
 
-        // The first classpath should never be null because the first classloader
-        // should always be an instance of BaseDexClassLoader.
-        String firstClassPath = classPaths.get(0);
-        if (firstClassPath == null) {
-            return;
-        }
-        // The classpath is represented as a list of dex files separated by File.pathSeparator.
-        String[] dexPathsToRegister = firstClassPath.split(File.pathSeparator);
-
-        // Encode the class loader contexts for the dexPathsToRegister.
-        String[] classLoaderContexts = DexoptUtils.processContextForDexLoad(
-                classLoaderNames, classPaths);
-
-        // A null classLoaderContexts means that there are unsupported class loaders in the
-        // chain.
-        if (classLoaderContexts == null) {
-            if (DEBUG) {
-                Slog.i(TAG, loadingAppInfo.packageName +
-                        " uses unsupported class loader in " + classLoaderNames);
-            }
-        }
-
-        int dexPathIndex = 0;
-        for (String dexPath : dexPathsToRegister) {
+        for (Map.Entry<String, String> mapping : classLoaderContextMap.entrySet()) {
+            String dexPath = mapping.getKey();
             // Find the owning package name.
             DexSearchResult searchResult = getDexPackage(loadingAppInfo, dexPath, loaderUserId);
 
@@ -229,7 +201,6 @@
                     // If the dex file is the primary apk (or a split) and not isUsedByOtherApps
                     // do not record it. This case does not bring any new usable information
                     // and can be safely skipped.
-                    dexPathIndex++;
                     continue;
                 }
 
@@ -239,13 +210,13 @@
                             searchResult.mOwningPackageName, loadingAppInfo.packageName);
                 }
 
-                if (classLoaderContexts != null) {
-
+                String classLoaderContext = mapping.getValue();
+                if (classLoaderContext != null
+                        && VMRuntime.isValidClassLoaderContext(classLoaderContext)) {
                     // Record dex file usage. If the current usage is a new pattern (e.g. new
                     // secondary, or UsedByOtherApps), record will return true and we trigger an
                     // async write to disk to make sure we don't loose the data in case of a reboot.
 
-                    String classLoaderContext = classLoaderContexts[dexPathIndex];
                     if (mPackageDexUsage.record(searchResult.mOwningPackageName,
                             dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
                             loadingAppInfo.packageName, classLoaderContext)) {
@@ -259,7 +230,6 @@
                     Slog.i(TAG, "Could not find owning package for dex file: " + dexPath);
                 }
             }
-            dexPathIndex++;
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index e68c238..08763e7 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -83,8 +83,9 @@
             "=UnknownClassLoaderContext=";
 
     // The marker used for unsupported class loader contexts (no longer written, may occur in old
-    // files so discarded on read).
-    private static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
+    // files so discarded on read). Note: this matches
+    // ClassLoaderContext::kUnsupportedClassLoaderContextEncoding in the runtime.
+    /*package*/ static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
             "=UnsupportedClassLoaderContext=";
 
     /**
@@ -133,6 +134,9 @@
         if (classLoaderContext == null) {
             throw new IllegalArgumentException("Null classLoaderContext");
         }
+        if (classLoaderContext.equals(UNSUPPORTED_CLASS_LOADER_CONTEXT)) {
+            return false;
+        }
 
         synchronized (mPackageUseInfoMap) {
             PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(owningPackageName);
@@ -843,10 +847,11 @@
             boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages);
 
             String oldClassLoaderContext = mClassLoaderContext;
-            if (UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext)) {
+            if (isUnknownOrUnsupportedContext(mClassLoaderContext)) {
                 // Can happen if we read a previous version.
                 mClassLoaderContext = dexUseInfo.mClassLoaderContext;
-            } else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
+            } else if (!isUnknownOrUnsupportedContext(dexUseInfo.mClassLoaderContext)
+                        && !Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
                 // We detected a context change.
                 mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT;
             }
@@ -857,6 +862,13 @@
                     || !Objects.equals(oldClassLoaderContext, mClassLoaderContext);
         }
 
+        private static boolean isUnknownOrUnsupportedContext(String context) {
+            // TODO: Merge UNKNOWN_CLASS_LOADER_CONTEXT & UNSUPPORTED_CLASS_LOADER_CONTEXT cases
+            // into UNSUPPORTED_CLASS_LOADER_CONTEXT.
+            return UNKNOWN_CLASS_LOADER_CONTEXT.equals(context)
+                    || UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(context);
+        }
+
         public boolean isUsedByOtherApps() {
             return mIsUsedByOtherApps;
         }
@@ -878,7 +890,7 @@
         public boolean isUnknownClassLoaderContext() {
             // The class loader context may be unknown if we loaded the data from a previous version
             // which didn't save the context.
-            return UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
+            return isUnknownOrUnsupportedContext(mClassLoaderContext);
         }
 
         public boolean isVariableClassLoaderContext() {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 3a7604a..26a623f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -23,6 +23,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.SynchronousUserSwitchObserver;
@@ -91,6 +92,7 @@
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
 import com.android.server.UiThread;
+import com.android.server.UserspaceRebootLogger;
 import com.android.server.Watchdog;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.lights.LightsManager;
@@ -2834,7 +2836,10 @@
     }
 
     private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
-            final String reason, boolean wait) {
+            @Nullable final String reason, boolean wait) {
+        if (PowerManager.REBOOT_USERSPACE.equals(reason)) {
+            UserspaceRebootLogger.noteUserspaceRebootWasRequested();
+        }
         if (mHandler == null || !mSystemReady) {
             if (RescueParty.isAttemptingFactoryReset()) {
                 // If we're stuck in a really low-level reboot loop, and a
@@ -4649,7 +4654,7 @@
          * @param wait If true, this call waits for the reboot to complete and does not return.
          */
         @Override // Binder call
-        public void reboot(boolean confirm, String reason, boolean wait) {
+        public void reboot(boolean confirm, @Nullable String reason, boolean wait) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
             if (PowerManager.REBOOT_RECOVERY.equals(reason)
                     || PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 81b42da..153b006 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -21,6 +21,7 @@
         "BroadcastRadio/convert.cpp",
         "BroadcastRadio/regions.cpp",
         "com_android_server_AlarmManagerService.cpp",
+        "com_android_server_adb_AdbDebuggingManager.cpp",
         "com_android_server_am_BatteryStatsService.cpp",
         "com_android_server_connectivity_Vpn.cpp",
         "com_android_server_ConsumerIrService.cpp",
@@ -69,6 +70,8 @@
 cc_defaults {
     name: "libservices.core-libs",
     shared_libs: [
+        "libadb_pairing_server",
+        "libadb_pairing_connection",
         "libandroid_runtime",
         "libandroidfw",
         "libaudioclient",
diff --git a/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp b/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp
new file mode 100644
index 0000000..aa50eec
--- /dev/null
+++ b/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AdbDebuggingManager-JNI"
+
+#define LOG_NDEBUG 0
+
+#include <algorithm>
+#include <condition_variable>
+#include <mutex>
+#include <optional>
+#include <random>
+#include <string>
+#include <vector>
+
+#include <adb/pairing/pairing_server.h>
+#include <android-base/properties.h>
+#include <utils/Log.h>
+
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+namespace {
+
+template <class T, class N>
+class JSmartWrapper {
+public:
+    JSmartWrapper(JNIEnv* env, T* jData) :
+        mEnv(env),
+        mJData(jData) {
+    }
+
+    virtual ~JSmartWrapper() = default;
+
+    const N* data() const {
+        return mRawData;
+    }
+
+    jsize size() const {
+        return mSize;
+    }
+
+protected:
+    N* mRawData = nullptr;
+    JNIEnv* mEnv = nullptr;
+    T* mJData = nullptr;
+    jsize mSize = 0;
+};  // JSmartWrapper
+
+class JStringUTFWrapper : public JSmartWrapper<jstring, const char> {
+public:
+    explicit JStringUTFWrapper(JNIEnv* env, jstring* str)
+        : JSmartWrapper(env, str) {
+        mRawData = env->GetStringUTFChars(*str, NULL);
+        mSize = env->GetStringUTFLength(*str);
+    }
+
+    virtual ~JStringUTFWrapper() {
+        if (data()) {
+            mEnv->ReleaseStringUTFChars(*mJData, mRawData);
+        }
+    }
+}; // JStringUTFWrapper
+
+struct ServerDeleter {
+    void operator()(PairingServerCtx* p) {
+        pairing_server_destroy(p);
+    }
+};
+using PairingServerPtr = std::unique_ptr<PairingServerCtx, ServerDeleter>;
+struct PairingResultWaiter {
+    std::mutex mutex_;
+    std::condition_variable cv_;
+    std::optional<bool> is_valid_;
+    PeerInfo peer_info_;
+
+    static void ResultCallback(const PeerInfo* peer_info,
+                               void* opaque) {
+        auto* p = reinterpret_cast<PairingResultWaiter*>(opaque);
+        {
+            std::unique_lock<std::mutex> lock(p->mutex_);
+            if (peer_info) {
+                memcpy(&(p->peer_info_), peer_info, sizeof(PeerInfo));
+            }
+            p->is_valid_ = (peer_info != nullptr);
+        }
+        p->cv_.notify_one();
+    }
+};
+
+PairingServerPtr sServer;
+std::unique_ptr<PairingResultWaiter> sWaiter;
+}  // namespace
+
+static jint native_pairing_start(JNIEnv* env,
+                                 jobject thiz,
+                                 jstring guid,
+                                 jstring password) {
+    // Server-side only sends its GUID on success.
+    PeerInfo system_info = {};
+    system_info.type = ADB_DEVICE_GUID;
+    JStringUTFWrapper guidWrapper(env, &guid);
+    memcpy(system_info.data, guidWrapper.data(), guidWrapper.size());
+
+    JStringUTFWrapper passwordWrapper(env, &password);
+
+    // Create the pairing server
+    sServer = PairingServerPtr(
+            pairing_server_new_no_cert(reinterpret_cast<const uint8_t*>(passwordWrapper.data()),
+                                       passwordWrapper.size(),
+                                       &system_info,
+                                       0));
+
+    sWaiter.reset(new PairingResultWaiter);
+    uint16_t port = pairing_server_start(sServer.get(), sWaiter->ResultCallback, sWaiter.get());
+    if (port == 0) {
+        ALOGE("Failed to start pairing server");
+        return -1;
+    }
+
+    return port;
+}
+
+static void native_pairing_cancel(JNIEnv* /* env */,
+                                  jclass /* clazz */) {
+    if (sServer != nullptr) {
+        sServer.reset();
+    }
+}
+
+static jboolean native_pairing_wait(JNIEnv* env,
+                                    jobject thiz) {
+    ALOGI("Waiting for pairing server to complete");
+    std::unique_lock<std::mutex> lock(sWaiter->mutex_);
+    if (!sWaiter->is_valid_.has_value()) {
+        sWaiter->cv_.wait(lock, [&]() { return sWaiter->is_valid_.has_value(); });
+    }
+    if (!*(sWaiter->is_valid_)) {
+        return JNI_FALSE;
+    }
+
+    std::string peer_public_key = reinterpret_cast<char*>(sWaiter->peer_info_.data);
+    // Write to PairingThread's member variables
+    jclass clazz = env->GetObjectClass(thiz);
+    jfieldID mPublicKey = env->GetFieldID(clazz, "mPublicKey", "Ljava/lang/String;");
+    jstring jpublickey = env->NewStringUTF(peer_public_key.c_str());
+    env->SetObjectField(thiz, mPublicKey, jpublickey);
+    return JNI_TRUE;
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod gPairingThreadMethods[] = {
+    /* name, signature, funcPtr */
+    { "native_pairing_start", "(Ljava/lang/String;Ljava/lang/String;)I",
+            (void*) native_pairing_start },
+    { "native_pairing_cancel", "()V",
+            (void*) native_pairing_cancel },
+    { "native_pairing_wait", "()Z",
+            (void*) native_pairing_wait },
+};
+
+int register_android_server_AdbDebuggingManager(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, "com/android/server/adb/AdbDebuggingManager$PairingThread",
+              gPairingThreadMethods, NELEM(gPairingThreadMethods));
+    (void) res;  // Faked use when LOG_NDEBUG.
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+    return 0;
+}
+
+} /* namespace android */
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 692c9d2..657920e 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -56,6 +56,7 @@
 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_android_server_AdbDebuggingManager(JNIEnv* env);
 };
 
 using namespace android;
@@ -105,5 +106,6 @@
     register_android_server_security_VerityUtils(env);
     register_android_server_am_AppCompactor(env);
     register_android_server_am_LowMemDetector(env);
+    register_android_server_AdbDebuggingManager(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 952a64c..03f64fc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -403,7 +403,7 @@
      *  System property whose value is either "true" or "false", indicating whether
      *  device owner is present.
      */
-    private static final String PROPERTY_DEVICE_OWNER_PRESENT = "ro.device_owner";
+    private static final String PROPERTY_DEVICE_OWNER_PRESENT = "ro.organization_owned";
 
     private static final int STATUS_BAR_DISABLE_MASK =
             StatusBarManager.DISABLE_EXPAND |
@@ -434,6 +434,7 @@
 
         GLOBAL_SETTINGS_WHITELIST = new ArraySet<>();
         GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_ENABLED);
+        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_WIFI_ENABLED);
         GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME);
         GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME_ZONE);
         GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.DATA_ROAMING);
@@ -2475,11 +2476,11 @@
         }
 
         if (!mInjector.systemPropertiesGet(PROPERTY_DEVICE_OWNER_PRESENT, "").isEmpty()) {
-            Slog.w(LOG_TAG, "Trying to set ro.device_owner, but it has already been set?");
+            Slog.w(LOG_TAG, "Trying to set ro.organization_owned, but it has already been set?");
         } else {
             final String value = Boolean.toString(hasDeviceOwner);
             mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, value);
-            Slog.i(LOG_TAG, "Set ro.device_owner property to " + value);
+            Slog.i(LOG_TAG, "Set ro.organization_owned property to " + value);
         }
     }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b93365a..670ec31 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -41,7 +41,6 @@
 import android.database.sqlite.SQLiteGlobal;
 import android.hardware.display.DisplayManagerInternal;
 import android.net.ConnectivityModuleConnector;
-import android.net.ITetheringConnector;
 import android.net.NetworkStackClient;
 import android.os.BaseBundle;
 import android.os.Binder;
@@ -286,6 +285,8 @@
     private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS =
             "com.android.server.contentsuggestions.ContentSuggestionsManagerService";
 
+    private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
+
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
 
     private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
@@ -938,7 +939,6 @@
                 false);
         boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
                 false);
-        boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false);
         boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false);
 
         boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
@@ -1910,7 +1910,7 @@
             traceEnd();
         }
 
-        if (!disableSlices) {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) {
             traceBeginAndSlog("StartSliceManagerService");
             mSystemServiceManager.startService(SLICE_MANAGER_SERVICE_CLASS);
             traceEnd();
@@ -2226,7 +2226,7 @@
             try {
                 // TODO: hide implementation details, b/146312721.
                 ConnectivityModuleConnector.getInstance().startModuleService(
-                        ITetheringConnector.class.getName(),
+                        TETHERING_CONNECTOR_CLASS,
                         PERMISSION_MAINLINE_NETWORK_STACK, service -> {
                             ServiceManager.addService(Context.TETHERING_SERVICE, service,
                                     false /* allowIsolated */,
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index 3ce514a..a08b3e7 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -26,6 +26,8 @@
         "services.core",
         "services.net",
     ],
+
+    libs: ["ike-stubs"],
 }
 
 //##################################################################
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 9d384e9..6fcc242 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -28,6 +28,8 @@
         "services.core",
         "services.net",
     ],
+
+    libs: ["ike-stubs"],
 }
 
 //##################################################################
diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
index d4182f3..c687ede 100644
--- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
@@ -672,6 +672,31 @@
                 connectionTime2, mKeyStore.getLastConnectionTime(TEST_KEY_2));
     }
 
+    @Test
+    public void testAdbKeyStore_removeKey() throws Exception {
+        // Accept the test key with the 'Always allow' option selected.
+        runAdbTest(TEST_KEY_1, true, true, false);
+        runAdbTest(TEST_KEY_2, true, true, false);
+
+        // Set the connection time to 0 to restore the original behavior.
+        setAllowedConnectionTime(0);
+
+        // Verify that the key is in the adb_keys file to ensure subsequent connections are
+        // automatically allowed by adbd.
+        persistKeyStore();
+        assertTrue("The key was not in the adb_keys file after persisting the keystore",
+                isKeyInFile(TEST_KEY_1, mAdbKeyFile));
+        assertTrue("The key was not in the adb_keys file after persisting the keystore",
+                isKeyInFile(TEST_KEY_2, mAdbKeyFile));
+
+        // Now remove one of the keys and make sure the other key is still there
+        mKeyStore.removeKey(TEST_KEY_1);
+        assertFalse("The key was still in the adb_keys file after removing the key",
+                isKeyInFile(TEST_KEY_1, mAdbKeyFile));
+        assertTrue("The key was not in the adb_keys file after removing a different key",
+                isKeyInFile(TEST_KEY_2, mAdbKeyFile));
+    }
+
     /**
      * Runs an adb test with the provided configuration.
      *
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index a4ba056..cb20b65 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -532,6 +532,61 @@
         assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
     }
 
+    @Test
+    public void testPrimaryAndSecondaryDexLoad() {
+        // Foo loads both primary and secondary dexes
+        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
+        List<String> fooDexes = new ArrayList<>(mFooUser0.getBaseAndSplitDexPaths());
+        int primaryCount = fooDexes.size();
+        fooDexes.addAll(fooSecondaries);
+
+        notifyDexLoad(mFooUser0, fooDexes, mUser0);
+
+        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+        assertIsUsedByOtherApps(mFooUser0, pui, false);
+        assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
+
+        // Below we want to verify that the secondary dex files within fooDexes have been correctly
+        // reported and their class loader contexts were correctly recorded.
+        //
+        // In order to achieve this we first use DexoptUtils.processContextForDexLoad to compute the
+        // class loader contexts for all the dex files.
+        String[] allClassLoaderContexts = DexoptUtils.processContextForDexLoad(
+                Arrays.asList(mFooUser0.mClassLoader),
+                Arrays.asList(String.join(File.pathSeparator, fooDexes)));
+        // Next we filter out the class loader contexts corresponding to non-secondary dex files.
+        String[] secondaryClassLoaderContexts = Arrays.copyOfRange(allClassLoaderContexts,
+                primaryCount, allClassLoaderContexts.length);
+        assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0,
+                secondaryClassLoaderContexts);
+
+        assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
+    }
+
+    @Test
+    public void testNotifySecondary_withSharedLibrary() {
+        // Foo loads its own secondary files.
+        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
+
+        String contextSuffix = "{PCL[/system/framework/org.apache.http.legacy.jar]}";
+        String[] expectedContexts = DexoptUtils.processContextForDexLoad(
+                Arrays.asList(mFooUser0.mClassLoader),
+                Arrays.asList(String.join(File.pathSeparator, fooSecondaries)));
+        for (int i = 0; i < expectedContexts.length; i++) {
+            expectedContexts[i] += contextSuffix;
+        }
+
+        notifyDexLoad(mFooUser0, fooSecondaries, expectedContexts, mUser0);
+
+        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+        assertIsUsedByOtherApps(mFooUser0, pui, false);
+        assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
+        assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0,
+                expectedContexts);
+
+        assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
+    }
+
     private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
             List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
             String[] expectedContexts) {
@@ -572,17 +627,43 @@
         // By default, assume a single class loader in the chain.
         // This makes writing tests much easier.
         List<String> classLoaders = Arrays.asList(testData.mClassLoader);
-        List<String> classPaths = (dexPaths == null)
-                                  ? Arrays.asList((String) null)
-                                  : Arrays.asList(String.join(File.pathSeparator, dexPaths));
+        List<String> classPaths = dexPaths != null
+                ? Arrays.<String>asList(String.join(File.pathSeparator, dexPaths)) : null;
         notifyDexLoad(testData, classLoaders, classPaths, loaderUserId);
     }
 
     private void notifyDexLoad(TestData testData, List<String> classLoaders,
             List<String> classPaths, int loaderUserId) {
+        String[] classLoaderContexts = computeClassLoaderContexts(classLoaders, classPaths);
         // We call the internal function so any exceptions thrown cause test failures.
-        mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, classLoaders,
-                classPaths, testData.mLoaderIsa, loaderUserId);
+        List<String> dexPaths = classPaths != null
+                ? Arrays.asList(classPaths.get(0).split(File.pathSeparator)) : Arrays.asList();
+        notifyDexLoad(testData, dexPaths, classLoaderContexts, loaderUserId);
+    }
+
+    private void notifyDexLoad(TestData testData, List<String> dexPaths,
+            String[] classLoaderContexts, int loaderUserId) {
+        assertTrue(dexPaths.size() == classLoaderContexts.length);
+        HashMap<String, String> dexPathMapping = new HashMap<>(dexPaths.size());
+        for (int i = 0; i < dexPaths.size(); i++) {
+            dexPathMapping.put(dexPaths.get(i), classLoaderContexts != null
+                    ? classLoaderContexts[i] : PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
+        }
+        mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, dexPathMapping,
+                testData.mLoaderIsa, loaderUserId);
+    }
+
+    private String[] computeClassLoaderContexts(List<String> classLoaders,
+            List<String> classPaths) {
+        if (classPaths == null) {
+            return new String[0];
+        }
+        String[] results = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
+        if (results == null) {
+            results = new String[classPaths.get(0).split(File.pathSeparator).length];
+            Arrays.fill(results, PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
+        }
+        return results;
     }
 
     private PackageUseInfo getPackageUseInfo(TestData testData) {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 45b840c..121a5b57 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -41,6 +41,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.debug.AdbManagerInternal;
+import android.debug.AdbTransportType;
 import android.debug.IAdbTransport;
 import android.hardware.usb.ParcelableUsbPort;
 import android.hardware.usb.UsbAccessory;
@@ -774,8 +775,10 @@
             }
 
             @Override
-            public void onAdbEnabled(boolean enabled) {
-                mHandler.sendMessage(MSG_ENABLE_ADB, enabled);
+            public void onAdbEnabled(boolean enabled, byte transportType) {
+                if (transportType == AdbTransportType.USB) {
+                    mHandler.sendMessage(MSG_ENABLE_ADB, enabled);
+                }
             }
         }
 
@@ -1169,7 +1172,8 @@
         }
 
         protected boolean isAdbEnabled() {
-            return LocalServices.getService(AdbManagerInternal.class).isAdbEnabled();
+            return LocalServices.getService(AdbManagerInternal.class)
+                    .isAdbEnabled(AdbTransportType.USB);
         }
 
         protected void updateAdbNotification(boolean force) {
@@ -1714,21 +1718,6 @@
         private static final int ENUMERATION_TIME_OUT_MS = 2000;
 
         /**
-         * Command to start native service.
-         */
-        protected static final String CTL_START = "ctl.start";
-
-        /**
-         * Command to start native service.
-         */
-        protected static final String CTL_STOP = "ctl.stop";
-
-        /**
-         * Adb native daemon.
-         */
-        protected static final String ADBD = "adbd";
-
-        /**
          * Gadget HAL fully qualified instance name for registering for ServiceNotification.
          */
         protected static final String GADGET_HAL_FQ_NAME =
@@ -1913,12 +1902,14 @@
                         /**
                          * Start adbd if ADB function is included in the configuration.
                          */
-                        setSystemProperty(CTL_START, ADBD);
+                        LocalServices.getService(AdbManagerInternal.class)
+                                .startAdbdForTransport(AdbTransportType.USB);
                     } else {
                         /**
-                         * Stop adbd otherwise.
+                         * Stop adbd otherwise
                          */
-                        setSystemProperty(CTL_STOP, ADBD);
+                        LocalServices.getService(AdbManagerInternal.class)
+                                .stopAdbdForTransport(AdbTransportType.USB);
                     }
                     UsbGadgetCallback usbGadgetCallback = new UsbGadgetCallback(mCurrentRequest,
                             config, chargingFunctions);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
old mode 100644
new mode 100755
index 52213d8..c5fcf67
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -465,8 +465,27 @@
          * @hide
          */
         public static final int CAPABILITY_ADD_PARTICIPANT = 0x02000000;
+
+        /**
+         * When set for a call, indicates that this {@code Call} can be transferred to another
+         * number.
+         * Call supports the blind and assured call transfer feature.
+         *
+         * @hide
+         */
+        public static final int CAPABILITY_TRANSFER = 0x04000000;
+
+        /**
+         * When set for a call, indicates that this {@code Call} can be transferred to another
+         * ongoing call.
+         * Call supports the consultative call transfer feature.
+         *
+         * @hide
+         */
+        public static final int CAPABILITY_TRANSFER_CONSULTATIVE = 0x08000000;
+
         //******************************************************************************************
-        // Next CAPABILITY value: 0x04000000
+        // Next CAPABILITY value: 0x10000000
         //******************************************************************************************
 
         /**
@@ -699,6 +718,12 @@
             if (can(capabilities, CAPABILITY_ADD_PARTICIPANT)) {
                 builder.append(" CAPABILITY_ADD_PARTICIPANT");
             }
+            if (can(capabilities, CAPABILITY_TRANSFER)) {
+                builder.append(" CAPABILITY_TRANSFER");
+            }
+            if (can(capabilities, CAPABILITY_TRANSFER_CONSULTATIVE)) {
+                builder.append(" CAPABILITY_TRANSFER_CONSULTATIVE");
+            }
             builder.append("]");
             return builder.toString();
         }
@@ -1564,6 +1589,30 @@
     }
 
     /**
+     * Instructs this {@code Call} to be transferred to another number.
+     *
+     * @param targetNumber The address to which the call will be transferred.
+     * @param isConfirmationRequired if {@code true} it will initiate ASSURED transfer,
+     * if {@code false}, it will initiate BLIND transfer.
+     *
+     * @hide
+     */
+    public void transfer(@NonNull Uri targetNumber, boolean isConfirmationRequired) {
+        mInCallAdapter.transferCall(mTelecomCallId, targetNumber, isConfirmationRequired);
+    }
+
+    /**
+     * Instructs this {@code Call} to be transferred to another ongoing call.
+     * This will initiate CONSULTATIVE transfer.
+     * @param toCall The other ongoing {@code Call} to which this call will be transferred.
+     *
+     * @hide
+     */
+    public void transfer(@NonNull android.telecom.Call toCall) {
+        mInCallAdapter.transferCall(mTelecomCallId, toCall.mTelecomCallId);
+    }
+
+    /**
      * Instructs this {@code Call} to disconnect.
      */
     public void disconnect() {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
old mode 100644
new mode 100755
index 3b0ba25..5f33a3d
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -387,8 +387,25 @@
      * @hide
      */
     public static final int CAPABILITY_ADD_PARTICIPANT = 0x04000000;
+
+    /**
+     * Indicates that this {@code Connection} can be transferred to another
+     * number.
+     * Connection supports the blind and assured call transfer feature.
+     * @hide
+     */
+    public static final int CAPABILITY_TRANSFER = 0x08000000;
+
+    /**
+     * Indicates that this {@code Connection} can be transferred to another
+     * ongoing {@code Connection}.
+     * Connection supports the consultative call transfer feature.
+     * @hide
+     */
+    public static final int CAPABILITY_TRANSFER_CONSULTATIVE = 0x10000000;
+
     //**********************************************************************************************
-    // Next CAPABILITY value: 0x08000000
+    // Next CAPABILITY value: 0x20000000
     //**********************************************************************************************
 
     /**
@@ -967,6 +984,13 @@
         if ((capabilities & CAPABILITY_ADD_PARTICIPANT) == CAPABILITY_ADD_PARTICIPANT) {
             builder.append(isLong ? " CAPABILITY_ADD_PARTICIPANT" : " add_participant");
         }
+        if ((capabilities & CAPABILITY_TRANSFER) == CAPABILITY_TRANSFER) {
+            builder.append(isLong ? " CAPABILITY_TRANSFER" : " sup_trans");
+        }
+        if ((capabilities & CAPABILITY_TRANSFER_CONSULTATIVE)
+                == CAPABILITY_TRANSFER_CONSULTATIVE) {
+            builder.append(isLong ? " CAPABILITY_TRANSFER_CONSULTATIVE" : " sup_cTrans");
+        }
         builder.append("]");
         return builder.toString();
     }
@@ -3092,6 +3116,26 @@
     public void onReject(String replyMessage) {}
 
     /**
+     * Notifies this Connection, a request to transfer to a target number.
+     * @param number the number to transfer this {@link Connection} to.
+     * @param isConfirmationRequired when {@code true}, the {@link ConnectionService}
+     * should wait until the transfer has successfully completed before disconnecting
+     * the current {@link Connection}.
+     * When {@code false}, the {@link ConnectionService} should signal the network to
+     * perform the transfer, but should immediately disconnect the call regardless of
+     * the outcome of the transfer.
+     * @hide
+     */
+    public void onTransfer(@NonNull Uri number, boolean isConfirmationRequired) {}
+
+    /**
+     * Notifies this Connection, a request to transfer to another Connection.
+     * @param otherConnection the {@link Connection} to transfer this call to.
+     * @hide
+     */
+    public void onTransfer(@NonNull Connection otherConnection) {}
+
+    /**
      * Notifies this Connection of a request to silence the ringer.
      * <p>
      * The ringer may be silenced by any of the following methods:
@@ -3314,7 +3358,6 @@
         private boolean mImmutable = false;
         public FailureSignalingConnection(DisconnectCause disconnectCause) {
             setDisconnected(disconnectCause);
-            mImmutable = true;
         }
 
         public void checkImmutable() {
@@ -3532,7 +3575,7 @@
      * ATIS-1000082.
      * @return the verification status.
      */
-    public @VerificationStatus int getCallerNumberVerificationStatus() {
+    public final @VerificationStatus int getCallerNumberVerificationStatus() {
         return mCallerNumberVerificationStatus;
     }
 
@@ -3544,7 +3587,7 @@
      * by
      * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}.
      */
-    public void setCallerNumberVerificationStatus(
+    public final void setCallerNumberVerificationStatus(
             @VerificationStatus int callerNumberVerificationStatus) {
         mCallerNumberVerificationStatus = callerNumberVerificationStatus;
     }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
old mode 100644
new mode 100755
index 2aea723..0dca006
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -128,6 +128,8 @@
     private static final String SESSION_ANSWER = "CS.an";
     private static final String SESSION_ANSWER_VIDEO = "CS.anV";
     private static final String SESSION_DEFLECT = "CS.def";
+    private static final String SESSION_TRANSFER = "CS.trans";
+    private static final String SESSION_CONSULTATIVE_TRANSFER = "CS.cTrans";
     private static final String SESSION_REJECT = "CS.r";
     private static final String SESSION_REJECT_MESSAGE = "CS.rWM";
     private static final String SESSION_SILENCE = "CS.s";
@@ -196,6 +198,8 @@
     private static final int MSG_CREATE_CONFERENCE_FAILED = 37;
     private static final int MSG_REJECT_WITH_REASON = 38;
     private static final int MSG_ADD_PARTICIPANT = 39;
+    private static final int MSG_EXPLICIT_CALL_TRANSFER = 40;
+    private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41;
 
     private static Connection sNullConnection;
 
@@ -481,6 +485,38 @@
         }
 
         @Override
+        public void transfer(@NonNull String callId, @NonNull Uri number,
+                boolean isConfirmationRequired, Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_TRANSFER);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = number;
+                args.argi1 = isConfirmationRequired ? 1 : 0;
+                args.arg3 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_EXPLICIT_CALL_TRANSFER, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
+        public void consultativeTransfer(@NonNull String callId, @NonNull String otherCallId,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_CONSULTATIVE_TRANSFER);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = otherCallId;
+                args.arg3 = Log.createSubsession();
+                mHandler.obtainMessage(
+                        MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
         public void silence(String callId, Session.Info sessionInfo) {
             Log.startSession(sessionInfo, SESSION_SILENCE);
             try {
@@ -1108,6 +1144,30 @@
                     }
                     break;
                 }
+                case MSG_EXPLICIT_CALL_TRANSFER: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_TRANSFER);
+                    try {
+                        final boolean isConfirmationRequired = args.argi1 == 1;
+                        transfer((String) args.arg1, (Uri) args.arg2, isConfirmationRequired);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
+                case MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Log.continueSession(
+                            (Session) args.arg3, SESSION_HANDLER + SESSION_CONSULTATIVE_TRANSFER);
+                    try {
+                        consultativeTransfer((String) args.arg1, (String) args.arg2);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
                 case MSG_DISCONNECT: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_DISCONNECT);
@@ -2042,6 +2102,18 @@
         findConnectionForAction(callId, "reject").onReject(rejectReason);
     }
 
+    private void transfer(String callId, Uri number, boolean isConfirmationRequired) {
+        Log.d(this, "transfer %s", callId);
+        findConnectionForAction(callId, "transfer").onTransfer(number, isConfirmationRequired);
+    }
+
+    private void consultativeTransfer(String callId, String otherCallId) {
+        Log.d(this, "consultativeTransfer %s", callId);
+        Connection connection1 = findConnectionForAction(callId, "consultativeTransfer");
+        Connection connection2 = findConnectionForAction(otherCallId, " consultativeTransfer");
+        connection1.onTransfer(connection2);
+    }
+
     private void silence(String callId) {
         Log.d(this, "silence %s", callId);
         findConnectionForAction(callId, "silence").onSilence();
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
old mode 100644
new mode 100755
index 9d291740..dd6c153
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
 
 package android.telecom;
 
+import android.annotation.NonNull;
 import android.bluetooth.BluetoothDevice;
 import android.net.Uri;
 import android.os.Bundle;
@@ -102,6 +103,35 @@
     }
 
     /**
+     * Instructs Telecom to transfer the specified call.
+     *
+     * @param callId The identifier of the call to transfer.
+     * @param targetNumber The address to transfer to.
+     * @param isConfirmationRequired if {@code true} it will initiate ASSURED transfer,
+     * if {@code false}, it will initiate BLIND transfer.
+     */
+    public void transferCall(@NonNull String callId, @NonNull Uri targetNumber,
+            boolean isConfirmationRequired) {
+        try {
+            mAdapter.transferCall(callId, targetNumber, isConfirmationRequired);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Instructs Telecom to transfer the specified call to another ongoing call.
+     *
+     * @param callId The identifier of the call to transfer.
+     * @param otherCallId The identifier of the other call to which this will be transferred.
+     */
+    public void transferCall(@NonNull String callId, @NonNull String otherCallId) {
+        try {
+            mAdapter.consultativeTransfer(callId, otherCallId);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Instructs Telecom to disconnect the specified call.
      *
      * @param callId The identifier of the call to disconnect.
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index a7024f9..19a1021e 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -326,6 +326,14 @@
             "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
 
     /**
+     * Optional extra for incoming and outgoing calls containing a long which specifies the Epoch
+     * time the call was created.
+     * @hide
+     */
+    public static final String EXTRA_CALL_CREATED_EPOCH_TIME_MILLIS =
+            "android.telecom.extra.CALL_CREATED_EPOCH_TIME_MILLIS";
+
+    /**
      * Optional extra for incoming and outgoing calls containing a long which specifies the time
      * telecom began routing the call. This value is in milliseconds since boot.
      * @hide
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index a397d77..fb54179 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -81,6 +81,11 @@
 
     void rejectWithMessage(String callId, String message, in Session.Info sessionInfo);
 
+    void transfer(String callId, in Uri number, boolean isConfirmationRequired,
+            in Session.Info sessionInfo);
+
+    void consultativeTransfer(String callId, String otherCallId, in Session.Info sessionInfo);
+
     void disconnect(String callId, in Session.Info sessionInfo);
 
     void silence(String callId, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
old mode 100644
new mode 100755
index 9beff22..edf1cf4
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -36,6 +36,10 @@
 
     void rejectCallWithReason(String callId, int rejectReason);
 
+    void transferCall(String callId, in Uri targetNumber, boolean isConfirmationRequired);
+
+    void consultativeTransfer(String callId, String otherCallId);
+
     void disconnectCall(String callId);
 
     void holdCall(String callId);
diff --git a/telephony/common/com/google/android/mms/util/DrmConvertSession.java b/telephony/common/com/google/android/mms/util/DrmConvertSession.java
index 156c7ad..17ab1547 100644
--- a/telephony/common/com/google/android/mms/util/DrmConvertSession.java
+++ b/telephony/common/com/google/android/mms/util/DrmConvertSession.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.drm.DrmConvertedStatus;
 import android.drm.DrmManagerClient;
-import android.provider.Downloads;
 import android.util.Log;
 
 import java.io.FileNotFoundException;
@@ -33,6 +32,13 @@
     private int mConvertSessionId;
     private static final String TAG = "DrmConvertSession";
 
+    // These values are copied from Downloads.Impl.* for backward compatibility since
+    // {@link #close()} that uses it is marked @UnsupportedAppUsage.
+    public static final int STATUS_SUCCESS = 200;
+    public static final int STATUS_NOT_ACCEPTABLE = 406;
+    public static final int STATUS_UNKNOWN_ERROR = 491;
+    public static final int STATUS_FILE_ERROR = 492;
+
     private DrmConvertSession(DrmManagerClient drmClient, int convertSessionId) {
         mDrmClient = drmClient;
         mConvertSessionId = convertSessionId;
@@ -118,38 +124,38 @@
      * Ends a conversion session of a file.
      *
      * @param fileName The filename of the converted file.
-     * @return Downloads.Impl.STATUS_SUCCESS if execution is ok.
-     *         Downloads.Impl.STATUS_FILE_ERROR in case converted file can not
-     *         be accessed. Downloads.Impl.STATUS_NOT_ACCEPTABLE if a problem
+     * @return STATUS_SUCCESS if execution is ok.
+     *         STATUS_FILE_ERROR in case converted file can not
+     *         be accessed. STATUS_NOT_ACCEPTABLE if a problem
      *         occurs when accessing drm framework.
-     *         Downloads.Impl.STATUS_UNKNOWN_ERROR if a general error occurred.
+     *         STATUS_UNKNOWN_ERROR if a general error occurred.
      */
     @UnsupportedAppUsage
     public int close(String filename) {
         DrmConvertedStatus convertedStatus = null;
-        int result = Downloads.Impl.STATUS_UNKNOWN_ERROR;
+        int result = STATUS_UNKNOWN_ERROR;
         if (mDrmClient != null && mConvertSessionId >= 0) {
             try {
                 convertedStatus = mDrmClient.closeConvertSession(mConvertSessionId);
                 if (convertedStatus == null ||
                         convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
                         convertedStatus.convertedData == null) {
-                    result = Downloads.Impl.STATUS_NOT_ACCEPTABLE;
+                    result = STATUS_NOT_ACCEPTABLE;
                 } else {
                     RandomAccessFile rndAccessFile = null;
                     try {
                         rndAccessFile = new RandomAccessFile(filename, "rw");
                         rndAccessFile.seek(convertedStatus.offset);
                         rndAccessFile.write(convertedStatus.convertedData);
-                        result = Downloads.Impl.STATUS_SUCCESS;
+                        result = STATUS_SUCCESS;
                     } catch (FileNotFoundException e) {
-                        result = Downloads.Impl.STATUS_FILE_ERROR;
+                        result = STATUS_FILE_ERROR;
                         Log.w(TAG, "File: " + filename + " could not be found.", e);
                     } catch (IOException e) {
-                        result = Downloads.Impl.STATUS_FILE_ERROR;
+                        result = STATUS_FILE_ERROR;
                         Log.w(TAG, "Could not access File: " + filename + " .", e);
                     } catch (IllegalArgumentException e) {
-                        result = Downloads.Impl.STATUS_FILE_ERROR;
+                        result = STATUS_FILE_ERROR;
                         Log.w(TAG, "Could not open file in mode: rw", e);
                     } catch (SecurityException e) {
                         Log.w(TAG, "Access to File: " + filename +
@@ -159,7 +165,7 @@
                             try {
                                 rndAccessFile.close();
                             } catch (IOException e) {
-                                result = Downloads.Impl.STATUS_FILE_ERROR;
+                                result = STATUS_FILE_ERROR;
                                 Log.w(TAG, "Failed to close File:" + filename
                                         + ".", e);
                             }
diff --git a/core/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl b/telephony/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl
rename to telephony/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 233eced..8cf113f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -319,6 +319,34 @@
             "only_auto_select_in_home_network";
 
     /**
+     * Flag indicating whether to show single operator row in the choose network setting.
+     *
+     * The device configuration value {@code config_enableNewAutoSelectNetworkUI} ultimately
+     * controls whether this carrier configuration option is used.  Where
+     * {@code config_enableNewAutoSelectNetworkUI} is false, the value of the
+     * {@link #KEY_SHOW_SINGLE_OPERATOR_ROW_IN_CHOOSE_NETWORK_SETTING_BOOL} carrier configuration
+     * option is ignored.
+     *
+     * If {@code true}, default value, merge the duplicate networks which with the same plmn, keep
+     * the one that with the higher signal strength level.
+     * If {@code false}, show all operators without merging.
+     * @hide
+     */
+    public static final String KEY_SHOW_SINGLE_OPERATOR_ROW_IN_CHOOSE_NETWORK_SETTING_BOOL =
+            "show_single_operator_row_in_choose_network_setting_bool";
+
+    /**
+     * Flag indicating whether to display SPN as network name for home network in choose
+     * network setting.
+     *
+     * If {@code true}, display SPN as network name in choose network setting.
+     * If {@code false}, display PLMN in choose network setting.
+     * @hide
+     */
+    public static final String KEY_SHOW_SPN_FOR_HOME_IN_CHOOSE_NETWORK_SETTING_BOOL =
+            "show_spn_for_home_in_choose_network_setting_bool";
+
+    /**
      * Control whether users receive a simplified network settings UI and improved network
      * selection.
      */
@@ -839,7 +867,8 @@
     /**
      * The default flag specifying whether ETWS/CMAS test setting is forcibly disabled in
      * Settings->More->Emergency broadcasts menu even though developer options is turned on.
-     * @deprecated moved to cellbroadcastreceiver resource show_test_settings
+     * @deprecated Use {@code com.android.cellbroadcastreceiver.CellBroadcastReceiver} resource
+     * {@code show_test_settings} to control whether to show test alert settings or not.
      */
     @Deprecated
     public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL =
@@ -1936,6 +1965,13 @@
             "carrier_allow_deflect_ims_call_bool";
 
     /**
+     * Flag indicating whether the carrier supports explicit call transfer for an IMS call.
+     * @hide
+     */
+    public static final String KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL =
+            "carrier_allow_transfer_ims_call_bool";
+
+    /**
      * Flag indicating whether the carrier always wants to play an "on-hold" tone when a call has
      * been remotely held.
      * <p>
@@ -1965,10 +2001,15 @@
             "allow_add_call_during_video_call";
 
     /**
-     * When false, indicates that holding a video call is disabled
+     * When {@code true}, indicates that video calls can be put on hold in order to swap to another
+     * call (e.g. a new outgoing call).
+     * When {@code false}, indicates that video calls will be disconnected when swapping to another
+     * call.
+     * <p>
+     * This is {@code true} by default.
      */
-    public static final String KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL =
-            "allow_holding_video_call";
+    public static final String KEY_ALLOW_HOLD_VIDEO_CALL_BOOL =
+            "allow_hold_video_call_bool";
 
     /**
      * When true, indicates that the HD audio icon in the in-call screen should not be shown for
@@ -3336,6 +3377,25 @@
             "subscription_group_uuid_string";
 
     /**
+     * Data switch validation minimal gap time, in milliseconds.
+     *
+     * Which means, if the same subscription on the same network (based on MCC+MNC+TAC+subId)
+     * was recently validated (within this time gap), and Telephony receives a request to switch to
+     * it again, Telephony will skip the validation part and switch to it as soon as connection
+     * is setup, as if it's already validated.
+     *
+     * If the network was validated within the gap but the latest validation result is false, the
+     * validation will not be skipped.
+     *
+     * If not set or set to 0, validation will never be skipped.
+     * The max acceptable value of this config is 24 hours.
+     *
+     * @hide
+     */
+    public static final String KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG =
+            "data_switch_validation_min_gap_LONG";
+
+    /**
     * A boolean property indicating whether this subscription should be managed as an opportunistic
     * subscription.
     *
@@ -3398,6 +3458,15 @@
             "carrier_certificate_string_array";
 
     /**
+     * Flag specifying whether the incoming call number should be formatted to national number
+     * for Japan. @return {@code true} convert to the national format, {@code false} otherwise.
+     * e.g. "+819012345678" -> "09012345678"
+     * @hide
+     */
+    public static final String KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL =
+            "format_incoming_number_to_national_for_jp_bool";
+
+    /**
      * DisconnectCause array to play busy tone. Value should be array of
      * {@link android.telephony.DisconnectCause}.
      */
@@ -3412,6 +3481,30 @@
     public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL =
             "prevent_clir_activation_and_deactivation_code_bool";
 
+    /**
+     * The list of originating address of missed incoming call SMS. If the SMS has originator
+     * matched, the SMS will be treated as special SMS for notifying missed incoming call to the
+     * user.
+     *
+     * @hide
+     */
+    public static final String KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY =
+            "missed_incoming_call_sms_originator_string_array";
+
+    /**
+     * The patterns of missed incoming call sms. This is the regular expression used for
+     * matching the missed incoming call's date, time, and caller id. The pattern should match
+     * fields for at least month, day, hour, and minute. Year is optional although it is encouraged.
+     *
+     * An usable pattern should look like this:
+     * ^(?<month>0[1-9]|1[012])\/(?<day>0[1-9]|1[0-9]|2[0-9]|3[0-1]) (?<hour>[0-1][0-9]|2[0-3]):
+     * (?<minute>[0-5][0-9])\s*(?<callerId>[0-9]+)\s*$
+     *
+     * @hide
+     */
+    public static final String KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY =
+            "missed_incoming_call_sms_pattern_string_array";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -3420,6 +3513,7 @@
         sDefaults.putString(KEY_CARRIER_CONFIG_VERSION_STRING, "");
         sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
         sDefaults.putBoolean(KEY_AUTO_RETRY_FAILED_WIFI_EMERGENCY_CALL, false);
         sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
@@ -3475,6 +3569,8 @@
         sDefaults.putBoolean(KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL, false);
         sDefaults.putBoolean(KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
         sDefaults.putBoolean(KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_SINGLE_OPERATOR_ROW_IN_CHOOSE_NETWORK_SETTING_BOOL, true);
+        sDefaults.putBoolean(KEY_SHOW_SPN_FOR_HOME_IN_CHOOSE_NETWORK_SETTING_BOOL, false);
         sDefaults.putBoolean(KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL, false);
         sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false);
 
@@ -3713,7 +3809,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_ALLOW_HOLD_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);
@@ -3896,12 +3992,17 @@
         sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
         sDefaults.putAll(Ims.getDefaults());
         sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null);
+         sDefaults.putBoolean(KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL, false);
         sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
                 new int[] {4 /* BUSY */});
         sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
         sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000);
         sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
                 CellSignalStrengthLte.USE_RSRP);
+        sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, 0);
+        sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY,
+                new String[0]);
+        sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
     }
 
     /**
@@ -4120,6 +4221,7 @@
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getDefaultCarrierServicePackageName ICarrierConfigLoader is null"
                     + ex.toString());
+            ex.rethrowAsRuntimeException();
         }
         return "";
     }
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 4978dc1..2529387 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -244,19 +244,30 @@
         } else {
             mParametersUseForLevel = cc.getInt(
                     CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT);
-            Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel);
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel);
+            }
             rsrpThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
             if (rsrpThresholds == null) rsrpThresholds = sRsrpThresholds;
-            Rlog.i(LOG_TAG, "Applying LTE RSRP Thresholds: " + Arrays.toString(rsrpThresholds));
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Applying LTE RSRP Thresholds: "
+                        + Arrays.toString(rsrpThresholds));
+            }
             rsrqThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY);
             if (rsrqThresholds == null) rsrqThresholds = sRsrqThresholds;
-            Rlog.i(LOG_TAG, "Applying LTE RSRQ Thresholds: " + Arrays.toString(rsrqThresholds));
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Applying LTE RSRQ Thresholds: "
+                        + Arrays.toString(rsrqThresholds));
+            }
             rssnrThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY);
             if (rssnrThresholds == null) rssnrThresholds = sRssnrThresholds;
-            Rlog.i(LOG_TAG, "Applying LTE RSSNR Thresholds: " + Arrays.toString(rssnrThresholds));
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Applying LTE RSSNR Thresholds: "
+                        + Arrays.toString(rssnrThresholds));
+            }
             rsrpOnly = cc.getBoolean(
                     CarrierConfigManager.KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
         }
@@ -283,15 +294,21 @@
 
         if (isLevelForParameter(USE_RSRP)) {
             rsrpLevel = updateLevelWithMeasure(rsrp, rsrpThresholds);
-            Rlog.i(LOG_TAG, "Updated 4G LTE RSRP Level: " + rsrpLevel);
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Updated 4G LTE RSRP Level: " + rsrpLevel);
+            }
         }
         if (isLevelForParameter(USE_RSRQ)) {
             rsrqLevel = updateLevelWithMeasure(mRsrq, rsrqThresholds);
-            Rlog.i(LOG_TAG, "Updated 4G LTE RSRQ Level: " + rsrqLevel);
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Updated 4G LTE RSRQ Level: " + rsrqLevel);
+            }
         }
         if (isLevelForParameter(USE_RSSNR)) {
             rssnrLevel = updateLevelWithMeasure(mRssnr, rssnrThresholds);
-            Rlog.i(LOG_TAG, "Updated 4G LTE RSSNR Level: " + rssnrLevel);
+            if (DBG) {
+                Rlog.i(LOG_TAG, "Updated 4G LTE RSSNR Level: " + rssnrLevel);
+            }
         }
         // Apply the smaller value among three levels of three measures.
         mLevel = Math.min(Math.min(rsrpLevel, rsrqLevel), rssnrLevel);
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index e3d03a3..8562df1 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -40,6 +40,8 @@
      */
     public static final int UNKNOWN_ASU_LEVEL = 99;
 
+    private static final boolean VDBG = false;
+
     private static final String TAG = "CellSignalStrengthNr";
 
     // Lifted from Default carrier configs and max range of SSRSRP
@@ -301,31 +303,45 @@
         } else {
             mParametersUseForLevel = cc.getInt(
                     CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, USE_SSRSRP);
-            Rlog.i(TAG, "Using SSRSRP for Level.");
             mSsRsrpThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY);
-            Rlog.i(TAG, "Applying 5G NR SSRSRP Thresholds: " + Arrays.toString(mSsRsrpThresholds));
+            if (VDBG) {
+                Rlog.i(TAG, "Applying 5G NR SSRSRP Thresholds: "
+                        + Arrays.toString(mSsRsrpThresholds));
+            }
             mSsRsrqThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY);
-            Rlog.i(TAG, "Applying 5G NR SSRSRQ Thresholds: " + Arrays.toString(mSsRsrqThresholds));
+            if (VDBG) {
+                Rlog.i(TAG, "Applying 5G NR SSRSRQ Thresholds: "
+                        + Arrays.toString(mSsRsrqThresholds));
+            }
             mSsSinrThresholds = cc.getIntArray(
                     CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY);
-            Rlog.i(TAG, "Applying 5G NR SSSINR Thresholds: " + Arrays.toString(mSsSinrThresholds));
+            if (VDBG) {
+                Rlog.i(TAG, "Applying 5G NR SSSINR Thresholds: "
+                        + Arrays.toString(mSsSinrThresholds));
+            }
         }
         int ssRsrpLevel = SignalStrength.INVALID;
         int ssRsrqLevel = SignalStrength.INVALID;
         int ssSinrLevel = SignalStrength.INVALID;
         if (isLevelForParameter(USE_SSRSRP)) {
             ssRsrpLevel = updateLevelWithMeasure(mSsRsrp, mSsRsrpThresholds);
-            Rlog.i(TAG, "Updated 5G NR SSRSRP Level: " + ssRsrpLevel);
+            if (VDBG) {
+                Rlog.i(TAG, "Updated 5G NR SSRSRP Level: " + ssRsrpLevel);
+            }
         }
         if (isLevelForParameter(USE_SSRSRQ)) {
             ssRsrqLevel = updateLevelWithMeasure(mSsRsrq, mSsRsrqThresholds);
-            Rlog.i(TAG, "Updated 5G NR SSRSRQ Level: " + ssRsrqLevel);
+            if (VDBG) {
+                Rlog.i(TAG, "Updated 5G NR SSRSRQ Level: " + ssRsrqLevel);
+            }
         }
         if (isLevelForParameter(USE_SSSINR)) {
             ssSinrLevel = updateLevelWithMeasure(mSsSinr, mSsSinrThresholds);
-            Rlog.i(TAG, "Updated 5G NR SSSINR Level: " + ssSinrLevel);
+            if (VDBG) {
+                Rlog.i(TAG, "Updated 5G NR SSSINR Level: " + ssSinrLevel);
+            }
         }
         // Apply the smaller value among three levels of three measures.
         mLevel = Math.min(Math.min(ssRsrpLevel, ssRsrqLevel), ssSinrLevel);
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index bd2375f..6f92406 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -199,7 +199,7 @@
     }
 
     /**
-     * Indicate if the ModemActivityInfo is invalid due to modem's invalid reporting.
+     * Indicates if the modem has reported valid {@link ModemActivityInfo}.
      *
      * @return {@code true} if this {@link ModemActivityInfo} record is valid,
      * {@code false} otherwise.
diff --git a/telephony/java/android/telephony/ModemInfo.java b/telephony/java/android/telephony/ModemInfo.java
new file mode 100644
index 0000000..c0833af
--- /dev/null
+++ b/telephony/java/android/telephony/ModemInfo.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Information of a single logical modem indicating
+ * its id, supported rats and whether it supports voice or data, etc.
+ * @hide
+ */
+public class ModemInfo implements Parcelable {
+    public final int modemId;
+    public final int rat; /* bitset */
+    public final boolean isVoiceSupported;
+    public final boolean isDataSupported;
+
+    // TODO b/121394331: Clean up this class after V1_1.PhoneCapability cleanup.
+    public ModemInfo(int modemId) {
+        this(modemId, 0, true, true);
+    }
+
+    public ModemInfo(int modemId, int rat, boolean isVoiceSupported, boolean isDataSupported) {
+        this.modemId = modemId;
+        this.rat = rat;
+        this.isVoiceSupported = isVoiceSupported;
+        this.isDataSupported = isDataSupported;
+    }
+
+    public ModemInfo(Parcel in) {
+        modemId = in.readInt();
+        rat = in.readInt();
+        isVoiceSupported = in.readBoolean();
+        isDataSupported = in.readBoolean();
+    }
+
+    @Override
+    public String toString() {
+        return "modemId=" + modemId + " rat=" + rat + " isVoiceSupported:" + isVoiceSupported
+                + " isDataSupported:" + isDataSupported;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(modemId, rat, isVoiceSupported, isDataSupported);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof ModemInfo) || hashCode() != o.hashCode()) {
+            return false;
+        }
+
+        if (this == o) {
+            return true;
+        }
+
+        ModemInfo s = (ModemInfo) o;
+
+        return (modemId == s.modemId
+                && rat == s.rat
+                && isVoiceSupported == s.isVoiceSupported
+                && isDataSupported == s.isDataSupported);
+    }
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    public @ContentsFlags int describeContents() {
+        return 0;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    public void writeToParcel(Parcel dest, @WriteFlags int flags) {
+        dest.writeInt(modemId);
+        dest.writeInt(rat);
+        dest.writeBoolean(isVoiceSupported);
+        dest.writeBoolean(isDataSupported);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ModemInfo> CREATOR = new Parcelable.Creator() {
+        public ModemInfo createFromParcel(Parcel in) {
+            return new ModemInfo(in);
+        }
+
+        public ModemInfo[] newArray(int size) {
+            return new ModemInfo[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java
index c66aceb..238766a 100644
--- a/telephony/java/android/telephony/PhoneCapability.java
+++ b/telephony/java/android/telephony/PhoneCapability.java
@@ -16,20 +16,12 @@
 
 package android.telephony;
 
-import android.annotation.LongDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.AccessNetworkConstants.AccessNetworkType;
-import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
-import android.telephony.TelephonyManager.NetworkTypeBitMask;
 
-import com.android.internal.telephony.util.TelephonyUtils;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
@@ -38,365 +30,68 @@
  * are shared between those modems defined by list of modem IDs.
  */
 public final class PhoneCapability implements Parcelable {
-    /** Modem feature indicating 3GPP2 capability. */
-    public static final long MODEM_FEATURE_3GPP2_REG = 1 << 0;
-    /** Modem feature indicating 3GPP capability. */
-    public static final long MODEM_FEATURE_3GPP_REG = 1 << 1;
-    /** Modem feature indicating CDMA 2000 with EHRPD capability. */
-    public static final long MODEM_FEATURE_CDMA2000_EHRPD_REG = 1 << 2;
-    /** Modem feature indicating GSM capability. */
-    public static final long MODEM_FEATURE_GERAN_REG = 1 << 3;
-    /** Modem feature indicating UMTS capability. */
-    public static final long MODEM_FEATURE_UTRAN_REG = 1 << 4;
-    /** Modem feature indicating LTE capability. */
-    public static final long MODEM_FEATURE_EUTRAN_REG = 1 << 5;
-    /** Modem feature indicating 5G capability.*/
-    public static final long MODEM_FEATURE_NGRAN_REG = 1 << 6;
-    /** Modem feature indicating EN-DC capability. */
-    public static final long MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG = 1 << 7;
-    /** Modem feature indicating VoLTE capability (IMS registered). */
-    public static final long MODEM_FEATURE_PS_VOICE_REG = 1 << 8;
-    /** Modem feature indicating CS voice call capability. */
-    public static final long MODEM_FEATURE_CS_VOICE_SESSION = 1 << 9;
-    /** Modem feature indicating Internet connection capability. */
-    public static final long MODEM_FEATURE_INTERACTIVE_DATA_SESSION = 1 << 10;
-    /**
-     * Modem feature indicating dedicated bearer capability.
-     * For services that require a high level QoS (eg. VoLTE), the network can create
-     * a dedicated bearer with the required QoS on top of an established default bearer.
-     * This will provide a dedicated tunnel for one or more specific traffic types.
-     */
-    public static final long MODEM_FEATURE_DEDICATED_BEARER = 1 << 11;
-    /** Modem feature indicating network scan capability. */
-    public static final long MODEM_FEATURE_NETWORK_SCAN = 1 << 12;
-    /** Modem feature indicating corresponding SIM has CDMA capability. */
-    public static final long MODEM_FEATURE_CSIM = 1 << 13;
-
+    // Hardcoded default DSDS capability.
     /** @hide */
-    @LongDef(flag = true, prefix = {"MODEM_FEATURE_" }, value = {
-            MODEM_FEATURE_3GPP2_REG,
-            MODEM_FEATURE_3GPP_REG,
-            MODEM_FEATURE_CDMA2000_EHRPD_REG,
-            MODEM_FEATURE_GERAN_REG,
-            MODEM_FEATURE_UTRAN_REG,
-            MODEM_FEATURE_EUTRAN_REG,
-            MODEM_FEATURE_NGRAN_REG,
-            MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG,
-            MODEM_FEATURE_PS_VOICE_REG,
-            MODEM_FEATURE_CS_VOICE_SESSION,
-            MODEM_FEATURE_INTERACTIVE_DATA_SESSION,
-            MODEM_FEATURE_DEDICATED_BEARER,
-            MODEM_FEATURE_NETWORK_SCAN,
-            MODEM_FEATURE_CSIM,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ModemFeature {
-    }
-
-    /**
-     * Hardcoded default DSDS capability.
-     * @hide
-     */
     public static final PhoneCapability DEFAULT_DSDS_CAPABILITY;
-    /**
-     * Hardcoded default Single SIM single standby capability.
-     * @hide
-     */
+    // Hardcoded default Single SIM single standby capability.
+    /** @hide */
     public static final PhoneCapability DEFAULT_SSSS_CAPABILITY;
 
     static {
-        List<List<Long>> capabilities = new ArrayList<>();
-        List<Long> modem1 = new ArrayList<>();
-        List<Long> modem2 = new ArrayList<>();
-        modem1.add(MODEM_FEATURE_GERAN_REG | MODEM_FEATURE_UTRAN_REG | MODEM_FEATURE_EUTRAN_REG
-                | MODEM_FEATURE_PS_VOICE_REG | MODEM_FEATURE_CS_VOICE_SESSION
-                | MODEM_FEATURE_INTERACTIVE_DATA_SESSION | MODEM_FEATURE_DEDICATED_BEARER);
-        modem2.add(MODEM_FEATURE_GERAN_REG | MODEM_FEATURE_UTRAN_REG | MODEM_FEATURE_EUTRAN_REG
-                | MODEM_FEATURE_PS_VOICE_REG | MODEM_FEATURE_INTERACTIVE_DATA_SESSION
-                | MODEM_FEATURE_DEDICATED_BEARER);
-        capabilities.add(modem1);
-        capabilities.add(modem2);
-        List<String> uuids = new ArrayList<>();
-        uuids.add("com.xxxx.lm0");
-        uuids.add("com.xxxx.lm1");
-        long rats = TelephonyManager.NETWORK_TYPE_BITMASK_GSM
-                | TelephonyManager.NETWORK_TYPE_BITMASK_GPRS
-                | TelephonyManager.NETWORK_TYPE_BITMASK_EDGE
-                | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS
-                | TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
-        DEFAULT_DSDS_CAPABILITY = new PhoneCapability(0, 0, 0, 0, 0, rats, null, null, null, null,
-                uuids, null, capabilities);
+        ModemInfo modemInfo1 = new ModemInfo(0, 0, true, true);
+        ModemInfo modemInfo2 = new ModemInfo(1, 0, true, true);
 
-        capabilities = new ArrayList<>();
-        capabilities.add(modem1);
-        uuids = new ArrayList<>();
-        uuids.add("com.xxxx.lm0");
-        DEFAULT_SSSS_CAPABILITY = new PhoneCapability(0, 0, 0, 0, 0, rats, null, null, null, null,
-                uuids, null, capabilities);
+        List<ModemInfo> logicalModemList = new ArrayList<>();
+        logicalModemList.add(modemInfo1);
+        logicalModemList.add(modemInfo2);
+        DEFAULT_DSDS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false);
+
+        logicalModemList = new ArrayList<>();
+        logicalModemList.add(modemInfo1);
+        DEFAULT_SSSS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false);
     }
+    /** @hide */
+    public final int maxActiveVoiceCalls;
+    /** @hide */
+    public final int maxActiveData;
+    /** @hide */
+    public final int max5G;
+    /** @hide */
+    public final boolean validationBeforeSwitchSupported;
+    /** @hide */
+    public final List<ModemInfo> logicalModemList;
 
-    private final int mUtranUeCategoryDl;
-    private final int mUtranUeCategoryUl;
-    private final int mEutranUeCategoryDl;
-    private final int mEutranUeCategoryUl;
-    private final long mPsDataConnectionLingerTimeMillis;
-    private final @NetworkTypeBitMask long mSupportedRats;
-    private final List<Integer> mGeranBands;
-    private final List<Integer> mUtranBands;
-    private final List<Integer> mEutranBands;
-    private final List<Integer> mNgranBands;
-    private final List<String> mLogicalModemUuids;
-    private final List<SimSlotCapability> mSimSlotCapabilities;
-    private final @ModemFeature List<List<Long>> mConcurrentFeaturesSupport;
-
-    /**
-     * Default constructor to create a PhoneCapability object.
-     * @param utranUeCategoryDl 3GPP UE category for UTRAN downlink.
-     * @param utranUeCategoryUl 3GPP UE category for UTRAN uplink.
-     * @param eutranUeCategoryDl 3GPP UE category for EUTRAN downlink.
-     * @param eutranUeCategoryUl 3GPP UE category for EUTRAN uplink.
-     * @param psDataConnectionLingerTimeMillis length of the grace period to allow a smooth
-     *                                         "handover" between data connections.
-     * @param supportedRats all radio access technologies this phone is capable of supporting.
-     * @param geranBands list of supported {@link AccessNetworkConstants.GeranBand}.
-     * @param utranBands list of supported {@link AccessNetworkConstants.UtranBand}.
-     * @param eutranBands list of supported {@link AccessNetworkConstants.EutranBand}.
-     * @param ngranBands list of supported {@link AccessNetworkConstants.NgranBands}.
-     * @param logicalModemUuids list of logical modem UUIDs, typically of the form
-     *                          "com.xxxx.lmX", where X is the logical modem ID.
-     * @param simSlotCapabilities list of {@link SimSlotCapability} for the device
-     * @param concurrentFeaturesSupport list of list of concurrently supportable modem feature sets.
-     * @hide
-     */
-    public PhoneCapability(int utranUeCategoryDl, int utranUeCategoryUl, int eutranUeCategoryDl,
-            int eutranUeCategoryUl, long psDataConnectionLingerTimeMillis,
-            @NetworkTypeBitMask long supportedRats, @Nullable List<Integer> geranBands,
-            @Nullable List<Integer> utranBands, @Nullable List<Integer> eutranBands,
-            @Nullable List<Integer> ngranBands, @Nullable List<String> logicalModemUuids,
-            @Nullable List<SimSlotCapability> simSlotCapabilities,
-            @Nullable @ModemFeature List<List<Long>> concurrentFeaturesSupport) {
-        this.mUtranUeCategoryDl = utranUeCategoryDl;
-        this.mUtranUeCategoryUl = utranUeCategoryUl;
-        this.mEutranUeCategoryDl = eutranUeCategoryDl;
-        this.mEutranUeCategoryUl = eutranUeCategoryUl;
-        this.mPsDataConnectionLingerTimeMillis = psDataConnectionLingerTimeMillis;
-        this.mSupportedRats = supportedRats;
-        this.mGeranBands = TelephonyUtils.emptyIfNull(geranBands);
-        this.mUtranBands = TelephonyUtils.emptyIfNull(utranBands);
-        this.mEutranBands = TelephonyUtils.emptyIfNull(eutranBands);
-        this.mNgranBands = TelephonyUtils.emptyIfNull(ngranBands);
-        this.mLogicalModemUuids = TelephonyUtils.emptyIfNull(logicalModemUuids);
-        this.mSimSlotCapabilities = TelephonyUtils.emptyIfNull(simSlotCapabilities);
-        this.mConcurrentFeaturesSupport = TelephonyUtils.emptyIfNull(concurrentFeaturesSupport);
-    }
-
-    private PhoneCapability(Parcel in) {
-        mUtranUeCategoryDl = in.readInt();
-        mUtranUeCategoryUl = in.readInt();
-        mEutranUeCategoryDl = in.readInt();
-        mEutranUeCategoryUl = in.readInt();
-        mPsDataConnectionLingerTimeMillis = in.readLong();
-        mSupportedRats = in.readLong();
-        mGeranBands = new ArrayList<>();
-        in.readList(mGeranBands, Integer.class.getClassLoader());
-        mUtranBands = new ArrayList<>();
-        in.readList(mUtranBands, Integer.class.getClassLoader());
-        mEutranBands = new ArrayList<>();
-        in.readList(mEutranBands, Integer.class.getClassLoader());
-        mNgranBands = new ArrayList<>();
-        in.readList(mNgranBands, Integer.class.getClassLoader());
-        mLogicalModemUuids = in.createStringArrayList();
-        mSimSlotCapabilities = in.createTypedArrayList(SimSlotCapability.CREATOR);
-        int length = in.readInt();
-        mConcurrentFeaturesSupport = new ArrayList<>();
-        for (int i = 0; i < length; i++) {
-            ArrayList<Long> feature = new ArrayList<>();
-            in.readList(feature, Long.class.getClassLoader());
-            mConcurrentFeaturesSupport.add(feature);
-        }
-    }
-
-    /**
-     * 3GPP UE category for a given Radio Access Network and direction.
-     *
-     * References are:
-     * TS 25.306 Table 4.1a     EUTRAN downlink
-     * TS 25.306 Table 5.1a-2   EUTRAN uplink
-     * TS 25.306 Table 5.1a     UTRAN downlink
-     * TS 25.306 Table 5.1g     UTRAN uplink
-     *
-     * @param uplink true for uplink direction and false for downlink direction.
-     * @param accessNetworkType accessNetworkType, defined in {@link AccessNetworkType}.
-     * @return the UE category, or -1 if it is not supported.
-     */
-    public int getUeCategory(boolean uplink, @RadioAccessNetworkType int accessNetworkType) {
-        if (uplink) {
-            switch (accessNetworkType) {
-                case AccessNetworkType.UTRAN: return mUtranUeCategoryUl;
-                case AccessNetworkType.EUTRAN: return mEutranUeCategoryUl;
-                default: return -1;
-            }
-        } else {
-            switch (accessNetworkType) {
-                case AccessNetworkType.UTRAN: return mUtranUeCategoryDl;
-                case AccessNetworkType.EUTRAN: return mEutranUeCategoryDl;
-                default: return -1;
-            }
-        }
-    }
-
-    /**
-     * In cellular devices that support a greater number of logical modems than
-     * Internet connections, some devices support a grace period to allow a smooth "handover"
-     * between those connections. If that feature is supported, then this API will provide
-     * the length of that grace period in milliseconds. If it is not supported, the default value
-     * for the grace period is 0.
-     * @return handover linger time in milliseconds, or 0 if it is not supported.
-     */
-    public long getPsDataConnectionLingerTimeMillis() {
-        return mPsDataConnectionLingerTimeMillis;
-    }
-
-    /**
-     * The radio access technologies this device is capable of supporting.
-     * @return a bitfield of all supported network types, defined in {@link TelephonyManager}
-     */
-    public @NetworkTypeBitMask long getSupportedRats() {
-        return mSupportedRats;
-    }
-
-    /**
-     * List of supported cellular bands for the given accessNetworkType.
-     * @param accessNetworkType accessNetworkType, defined in {@link AccessNetworkType}.
-     * @return a list of bands, or an empty list if the access network type is unsupported.
-     */
-    public @NonNull List<Integer> getBands(@RadioAccessNetworkType int accessNetworkType) {
-        switch (accessNetworkType) {
-            case AccessNetworkType.GERAN: return mGeranBands;
-            case AccessNetworkType.UTRAN: return mUtranBands;
-            case AccessNetworkType.EUTRAN: return mEutranBands;
-            case AccessNetworkType.NGRAN: return mNgranBands;
-            default: return new ArrayList<>();
-        }
-    }
-
-    /**
-     * List of logical modem UUIDs, each typically "com.xxxx.lmX", where X is the logical modem ID.
-     * @return a list of modem UUIDs, one for every logical modem the device has.
-     */
-    public @NonNull List<String> getLogicalModemUuids() {
-        return mLogicalModemUuids;
-    }
-
-    /**
-     * List of {@link SimSlotCapability} for the device. The order of SIMs corresponds to the
-     * order of modems in {@link #getLogicalModemUuids}.
-     * @return a list of SIM slot capabilities, one for every SIM slot the device has.
-     */
-    public @NonNull List<SimSlotCapability> getSimSlotCapabilities() {
-        return mSimSlotCapabilities;
-    }
-
-    /**
-     * A List of Lists of concurrently supportable modem feature sets.
-     *
-     * Each entry in the top-level list is an independent configuration across all modems
-     * that describes the capabilities of the device as a whole.
-     *
-     * Each entry in the second-level list is a bitfield of ModemFeatures that describes
-     * the capabilities for a single modem. In the second-level list, the order of the modems
-     * corresponds to order of the UUIDs in {@link #getLogicalModemUuids}.
-     *
-     * For symmetric capabilities that can only be active on one modem at a time, there will be
-     * multiple configurations (equal to the number of modems) that shows it active on each modem.
-     * For asymmetric capabilities that are only available on one of the modems, all configurations
-     * will have that capability on just that one modem.
-     *
-     * The example below shows the concurrentFeaturesSupport for a 3-modem device with
-     * theoretical capabilities SYMMETRIC (available on all modems, but only one at a time) and
-     * ASYMMETRIC (only available on the first modem):
-     * {
-     *      Configuration 1: ASYMMETRIC and SYMMETRIC on modem 1, modem 2 empty, modem 3 empty
-     *      {(ASYMMETRIC | SYMMETRIC), (), ()},
-     *
-     *      Configuration 2: ASYMMETRIC on modem 1, SYMMETRIC on modem 2, modem 3 empty
-     *      {(ASYMMETRIC), (SYMMETRIC), ()},
-     *
-     *      Configuration 3: ASYMMETRIC on modem 1, modem 2 empty, SYMMETRIC on modem 3
-     *      {(ASYMMETRIC), (), (SYMMETRIC)}
-     * }
-     *
-     * @return List of all concurrently supportable modem features.
-     */
-    public @NonNull @ModemFeature List<List<Long>> getConcurrentFeaturesSupport() {
-        return mConcurrentFeaturesSupport;
-    }
-
-    /**
-     * How many modems can simultaneously have PS attached.
-     * @return maximum number of active PS voice connections.
-     */
-    public int getMaxActivePsVoice() {
-        return countFeature(MODEM_FEATURE_PS_VOICE_REG);
-    }
-
-    /**
-     * How many modems can simultaneously support active data connections.
-     * For DSDS, this will be 1, and for DSDA this will be 2.
-     * @return maximum number of active Internet data sessions.
-     */
-    public int getMaxActiveInternetData() {
-        return countFeature(MODEM_FEATURE_INTERACTIVE_DATA_SESSION);
-    }
-
-    /**
-     * How many modems can simultaneously have dedicated bearer capability.
-     * @return maximum number of active dedicated bearers.
-     */
-    public int getMaxActiveDedicatedBearers() {
-        return countFeature(MODEM_FEATURE_DEDICATED_BEARER);
-    }
-
-    /**
-     * Whether the CBRS band 48 is supported or not.
-     * @return true if any RadioAccessNetwork supports CBRS and false if none do.
-     * @hide
-     */
-    public boolean isCbrsSupported() {
-        return mEutranBands.contains(AccessNetworkConstants.EutranBand.BAND_48)
-                || mNgranBands.contains(AccessNetworkConstants.NgranBands.BAND_48);
-    }
-
-    private int countFeature(@ModemFeature long feature) {
-        int count = 0;
-        for (long featureSet : mConcurrentFeaturesSupport.get(0)) {
-            if ((featureSet & feature) != 0) {
-                count++;
-            }
-        }
-        return count;
+    /** @hide */
+    public PhoneCapability(int maxActiveVoiceCalls, int maxActiveData, int max5G,
+            List<ModemInfo> logicalModemList, boolean validationBeforeSwitchSupported) {
+        this.maxActiveVoiceCalls = maxActiveVoiceCalls;
+        this.maxActiveData = maxActiveData;
+        this.max5G = max5G;
+        // Make sure it's not null.
+        this.logicalModemList = logicalModemList == null ? new ArrayList<>() : logicalModemList;
+        this.validationBeforeSwitchSupported = validationBeforeSwitchSupported;
     }
 
     @Override
     public String toString() {
-        return "utranUeCategoryDl=" + mUtranUeCategoryDl
-                + " utranUeCategoryUl=" + mUtranUeCategoryUl
-                + " eutranUeCategoryDl=" + mEutranUeCategoryDl
-                + " eutranUeCategoryUl=" + mEutranUeCategoryUl
-                + " psDataConnectionLingerTimeMillis=" + mPsDataConnectionLingerTimeMillis
-                + " supportedRats=" + mSupportedRats + " geranBands=" + mGeranBands
-                + " utranBands=" + mUtranBands + " eutranBands=" + mEutranBands
-                + " ngranBands=" + mNgranBands + " logicalModemUuids=" + mLogicalModemUuids
-                + " simSlotCapabilities=" + mSimSlotCapabilities
-                + " concurrentFeaturesSupport=" + mConcurrentFeaturesSupport;
+        return "maxActiveVoiceCalls=" + maxActiveVoiceCalls + " maxActiveData=" + maxActiveData
+                + " max5G=" + max5G + "logicalModemList:"
+                + Arrays.toString(logicalModemList.toArray());
+    }
+
+    private PhoneCapability(Parcel in) {
+        maxActiveVoiceCalls = in.readInt();
+        maxActiveData = in.readInt();
+        max5G = in.readInt();
+        validationBeforeSwitchSupported = in.readBoolean();
+        logicalModemList = new ArrayList<>();
+        in.readList(logicalModemList, ModemInfo.class.getClassLoader());
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mUtranUeCategoryDl, mUtranUeCategoryUl, mEutranUeCategoryDl,
-                mEutranUeCategoryUl, mPsDataConnectionLingerTimeMillis, mSupportedRats, mGeranBands,
-                mUtranBands, mEutranBands, mNgranBands, mLogicalModemUuids, mSimSlotCapabilities,
-                mConcurrentFeaturesSupport);
+        return Objects.hash(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList,
+                validationBeforeSwitchSupported);
     }
 
     @Override
@@ -411,19 +106,11 @@
 
         PhoneCapability s = (PhoneCapability) o;
 
-        return (mUtranUeCategoryDl == s.mUtranUeCategoryDl
-                && mUtranUeCategoryUl == s.mUtranUeCategoryUl
-                && mEutranUeCategoryDl == s.mEutranUeCategoryDl
-                && mEutranUeCategoryUl == s.mEutranUeCategoryUl
-                && mPsDataConnectionLingerTimeMillis == s.mPsDataConnectionLingerTimeMillis
-                && mSupportedRats == s.mSupportedRats
-                && mGeranBands.equals(s.mGeranBands)
-                && mUtranBands.equals(s.mUtranBands)
-                && mEutranBands.equals(s.mEutranBands)
-                && mNgranBands.equals(s.mNgranBands)
-                && mLogicalModemUuids.equals(s.mLogicalModemUuids)
-                && mSimSlotCapabilities.equals(s.mSimSlotCapabilities)
-                && mConcurrentFeaturesSupport.equals(s.mConcurrentFeaturesSupport));
+        return (maxActiveVoiceCalls == s.maxActiveVoiceCalls
+                && maxActiveData == s.maxActiveData
+                && max5G == s.max5G
+                && validationBeforeSwitchSupported == s.validationBeforeSwitchSupported
+                && logicalModemList.equals(s.logicalModemList));
     }
 
     /**
@@ -437,32 +124,20 @@
      * {@link Parcelable#writeToParcel}
      */
     public void writeToParcel(@NonNull Parcel dest, @Parcelable.WriteFlags int flags) {
-        dest.writeInt(mUtranUeCategoryDl);
-        dest.writeInt(mUtranUeCategoryUl);
-        dest.writeInt(mEutranUeCategoryDl);
-        dest.writeInt(mEutranUeCategoryUl);
-        dest.writeLong(mPsDataConnectionLingerTimeMillis);
-        dest.writeLong(mSupportedRats);
-        dest.writeList(mGeranBands);
-        dest.writeList(mUtranBands);
-        dest.writeList(mEutranBands);
-        dest.writeList(mNgranBands);
-        dest.writeStringList(mLogicalModemUuids);
-        dest.writeTypedList(mSimSlotCapabilities);
-        dest.writeInt(mConcurrentFeaturesSupport.size());
-        for (List<Long> feature : mConcurrentFeaturesSupport) {
-            dest.writeList(feature);
-        }
+        dest.writeInt(maxActiveVoiceCalls);
+        dest.writeInt(maxActiveData);
+        dest.writeInt(max5G);
+        dest.writeBoolean(validationBeforeSwitchSupported);
+        dest.writeList(logicalModemList);
     }
 
-    public static final @NonNull Parcelable.Creator<PhoneCapability> CREATOR =
-            new Parcelable.Creator() {
-                public PhoneCapability createFromParcel(Parcel in) {
-                    return new PhoneCapability(in);
-                }
+    public static final @android.annotation.NonNull Parcelable.Creator<PhoneCapability> CREATOR = new Parcelable.Creator() {
+        public PhoneCapability createFromParcel(Parcel in) {
+            return new PhoneCapability(in);
+        }
 
-                public PhoneCapability[] newArray(int size) {
-                    return new PhoneCapability[size];
-                }
-            };
+        public PhoneCapability[] newArray(int size) {
+            return new PhoneCapability[size];
+        }
+    };
 }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 9ef361d..a5a1ebc 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -317,6 +317,14 @@
      */
     public static final int UNKNOWN_ID = -1;
 
+    /**
+     * A parcelable extra used with {@link Intent#ACTION_SERVICE_STATE} representing the service
+     * state.
+     * @hide
+     */
+    private static final String EXTRA_SERVICE_STATE = "android.intent.extra.SERVICE_STATE";
+
+
     private String mOperatorAlphaLong;
     private String mOperatorAlphaShort;
     private String mOperatorNumeric;
@@ -353,6 +361,7 @@
 
     private String mOperatorAlphaLongRaw;
     private String mOperatorAlphaShortRaw;
+    private boolean mIsDataRoamingFromRegistration;
     private boolean mIsIwlanPreferred;
 
     /**
@@ -438,6 +447,7 @@
         mNrFrequencyRange = s.mNrFrequencyRange;
         mOperatorAlphaLongRaw = s.mOperatorAlphaLongRaw;
         mOperatorAlphaShortRaw = s.mOperatorAlphaShortRaw;
+        mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration;
         mIsIwlanPreferred = s.mIsIwlanPreferred;
     }
 
@@ -472,6 +482,7 @@
         mNrFrequencyRange = in.readInt();
         mOperatorAlphaLongRaw = in.readString();
         mOperatorAlphaShortRaw = in.readString();
+        mIsDataRoamingFromRegistration = in.readBoolean();
         mIsIwlanPreferred = in.readBoolean();
     }
 
@@ -499,6 +510,7 @@
         out.writeInt(mNrFrequencyRange);
         out.writeString(mOperatorAlphaLongRaw);
         out.writeString(mOperatorAlphaShortRaw);
+        out.writeBoolean(mIsDataRoamingFromRegistration);
         out.writeBoolean(mIsIwlanPreferred);
     }
 
@@ -649,7 +661,9 @@
     }
 
     /**
-     * Get current data network roaming type
+     * Get whether the current data network is roaming.
+     * This value may be overwritten by resource overlay or carrier configuration.
+     * @see #getDataRoamingFromRegistration() to get the value from the network registration.
      * @return roaming type
      * @hide
      */
@@ -659,18 +673,25 @@
     }
 
     /**
-     * Get whether data network registration state is roaming
+     * Set whether the data network registration state is roaming.
+     * This should only be set to the roaming value received
+     * once the data registration phase has completed.
+     * @hide
+     */
+    public void setDataRoamingFromRegistration(boolean dataRoaming) {
+        mIsDataRoamingFromRegistration = dataRoaming;
+    }
+
+    /**
+     * Get whether data network registration state is roaming.
+     * This value is set directly from the modem and will not be overwritten
+     * by resource overlay or carrier configuration.
      * @return true if registration indicates roaming, false otherwise
      * @hide
      */
+    @SystemApi
     public boolean getDataRoamingFromRegistration() {
-        final NetworkRegistrationInfo regState = getNetworkRegistrationInfo(
-                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-        if (regState != null) {
-            return regState.getRegistrationState()
-                    == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
-        }
-        return false;
+        return mIsDataRoamingFromRegistration;
     }
 
     /**
@@ -873,6 +894,7 @@
                     mNrFrequencyRange,
                     mOperatorAlphaLongRaw,
                     mOperatorAlphaShortRaw,
+                    mIsDataRoamingFromRegistration,
                     mIsIwlanPreferred);
         }
     }
@@ -903,6 +925,7 @@
                     && mNetworkRegistrationInfos.size() == s.mNetworkRegistrationInfos.size()
                     && mNetworkRegistrationInfos.containsAll(s.mNetworkRegistrationInfos)
                     && mNrFrequencyRange == s.mNrFrequencyRange
+                    && mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration
                     && mIsIwlanPreferred == s.mIsIwlanPreferred;
         }
     }
@@ -1062,6 +1085,8 @@
                     .append(", mNrFrequencyRange=").append(mNrFrequencyRange)
                     .append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw)
                     .append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw)
+                    .append(", mIsDataRoamingFromRegistration=")
+                    .append(mIsDataRoamingFromRegistration)
                     .append(", mIsIwlanPreferred=").append(mIsIwlanPreferred)
                     .append("}").toString();
         }
@@ -1102,6 +1127,7 @@
         }
         mOperatorAlphaLongRaw = null;
         mOperatorAlphaShortRaw = null;
+        mIsDataRoamingFromRegistration = false;
         mIsIwlanPreferred = false;
     }
 
@@ -1274,7 +1300,7 @@
      */
     @UnsupportedAppUsage
     private void setFromNotifierBundle(Bundle m) {
-        ServiceState ssFromBundle = m.getParcelable(Intent.EXTRA_SERVICE_STATE);
+        ServiceState ssFromBundle = m.getParcelable(EXTRA_SERVICE_STATE);
         if (ssFromBundle != null) {
             copyFrom(ssFromBundle);
         }
@@ -1292,7 +1318,7 @@
      */
     @SystemApi
     public void fillInNotifierBundle(@NonNull Bundle m) {
-        m.putParcelable(Intent.EXTRA_SERVICE_STATE, this);
+        m.putParcelable(EXTRA_SERVICE_STATE, this);
         // serviceState already consists of below entries.
         // for backward compatibility, we continue fill in below entries.
         m.putInt("voiceRegState", mVoiceRegState);
diff --git a/telephony/java/android/telephony/SimSlotCapability.java b/telephony/java/android/telephony/SimSlotCapability.java
deleted file mode 100644
index 3d38d04..0000000
--- a/telephony/java/android/telephony/SimSlotCapability.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * Capabilities for a SIM Slot.
- */
-public final class SimSlotCapability implements Parcelable {
-    /** Slot type for UICC (removable SIM). */
-    public static final int SLOT_TYPE_UICC = 1;
-    /** Slot type for iUICC/iSIM (integrated SIM). */
-    public static final int SLOT_TYPE_IUICC = 2;
-    /** Slot type for eUICC/eSIM (embedded SIM). */
-    public static final int SLOT_TYPE_EUICC = 3;
-    /** Slot type for soft SIM (no physical SIM). */
-    public static final int SLOT_TYPE_SOFT_SIM = 4;
-
-    /** @hide */
-    @IntDef(prefix = {"SLOT_TYPE_" }, value = {
-            SLOT_TYPE_UICC,
-            SLOT_TYPE_IUICC,
-            SLOT_TYPE_EUICC,
-            SLOT_TYPE_SOFT_SIM,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SlotType {
-    }
-
-    private final int mPhysicalSlotIndex;
-    private final int mSlotType;
-
-    /** @hide */
-    public SimSlotCapability(int physicalSlotId, int slotType) {
-        this.mPhysicalSlotIndex = physicalSlotId;
-        this.mSlotType = slotType;
-    }
-
-    private SimSlotCapability(Parcel in) {
-        mPhysicalSlotIndex = in.readInt();
-        mSlotType = in.readInt();
-    }
-
-    /**
-     * @return physical SIM slot index
-     */
-    public int getPhysicalSlotIndex() {
-        return mPhysicalSlotIndex;
-    }
-
-    /**
-     * @return type of SIM {@link SlotType}
-     */
-    public @SlotType int getSlotType() {
-        return mSlotType;
-    }
-
-    @Override
-    public String toString() {
-        return "mPhysicalSlotIndex=" + mPhysicalSlotIndex + " slotType=" + mSlotType;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mPhysicalSlotIndex, mSlotType);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o == null || !(o instanceof SimSlotCapability) || hashCode() != o.hashCode()) {
-            return false;
-        }
-
-        if (this == o) {
-            return true;
-        }
-
-        SimSlotCapability s = (SimSlotCapability) o;
-
-        return (mPhysicalSlotIndex == s.mPhysicalSlotIndex && mSlotType == s.mSlotType);
-    }
-
-    /**
-     * {@link Parcelable#describeContents}
-     */
-    public @ContentsFlags int describeContents() {
-        return 0;
-    }
-
-    /**
-     * {@link Parcelable#writeToParcel}
-     */
-    public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) {
-        dest.writeInt(mPhysicalSlotIndex);
-        dest.writeInt(mSlotType);
-    }
-
-    public static final @NonNull Parcelable.Creator<SimSlotCapability> CREATOR =
-            new Parcelable.Creator() {
-                public SimSlotCapability createFromParcel(Parcel in) {
-                    return new SimSlotCapability(in);
-                }
-
-                public SimSlotCapability[] newArray(int size) {
-                    return new SimSlotCapability[size];
-                }
-            };
-}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index e7b2613..f86eeb2 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2015,8 +2015,12 @@
 
     /**
      * Gets the total capacity of SMS storage on RUIM and SIM cards
+     * <p>
+     * This is the number of 176 byte EF-SMS records which can be stored on the RUIM or SIM card.
+     * <p>
+     * See 3GPP TS 31.102 - 4.2.25 - EF-SMS for more information
      *
-     * @return the total capacity count of SMS on RUIM and SIM cards
+     * @return the total number of SMS records which can be stored on the RUIM or SIM cards.
      * @hide
      */
     @SystemApi
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 1cf8f1e..ef03545 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -514,13 +514,6 @@
     public static final String ISO_COUNTRY_CODE = SimInfo.ISO_COUNTRY_CODE;
 
     /**
-     * TelephonyProvider column name for the sim provisioning status associated with a SIM.
-     * <P>Type: INTEGER (int)</P>
-     * @hide
-     */
-    public static final String SIM_PROVISIONING_STATUS = SimInfo.SIM_PROVISIONING_STATUS;
-
-    /**
      * TelephonyProvider column name for whether a subscription is embedded (that is, present on an
      * eSIM).
      * <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded.
@@ -1303,6 +1296,11 @@
      * The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex}
      * then by {@link SubscriptionInfo#getSubscriptionId}.
      *
+     * Hidden subscriptions refer to those are not meant visible to the users.
+     * For example, an opportunistic subscription that is grouped with other
+     * subscriptions should remain invisible to users as they are only functionally
+     * supplementary to primary ones.
+     *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see
      * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, only records accessible
@@ -2153,20 +2151,35 @@
     }
 
     /**
-     * TODO(b/137102918) Make this static, tests use this as an instance method currently.
+     * Get visible subscription Id(s) of the currently active SIM(s).
      *
      * @return the list of subId's that are active,
-     *         is never null but the length maybe 0.
+     *         is never null but the length may be 0.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public @NonNull int[] getActiveSubscriptionIdList() {
         return getActiveSubscriptionIdList(/* visibleOnly */ true);
     }
 
     /**
-     * TODO(b/137102918) Make this static, tests use this as an instance method currently.
+     * Get both hidden and visible subscription Id(s) of the currently active SIM(s).
      *
+     * Hidden subscriptions refer to those are not meant visible to the users.
+     * For example, an opportunistic subscription that is grouped with other
+     * subscriptions should remain invisible to users as they are only functionally
+     * supplementary to primary ones.
+     *
+     * @return the list of subId's that are active,
+     *         is never null but the length may be 0.
+     * @hide
+     */
+    @SystemApi
+    public @NonNull int[] getActiveAndHiddenSubscriptionIdList() {
+        return getActiveSubscriptionIdList(/* visibleOnly */false);
+    }
+
+    /**
      * @return a non-null list of subId's that are active.
      *
      * @hide
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2c85b27..7f7833b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -45,10 +45,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.net.ConnectivityManager;
-import android.net.NetworkStats;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -1725,24 +1723,6 @@
     //
 
     /**
-     * Returns the {@link PhoneCapability} for the device or null if it is not available.
-     * <p>
-     * Requires Permission: READ_PHONE_STATE or that the calling app has
-     * carrier privileges (see {@link #hasCarrierPrivileges}).
-     */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    @Nullable
-    public PhoneCapability getPhoneCapability() {
-        try {
-            ITelephony telephony = getITelephony();
-            return telephony == null ? null :
-                    telephony.getPhoneCapability(getSubId(), getOpPackageName(), getFeatureId());
-        } catch (RemoteException ex) {
-            return null;
-        }
-    }
-
-    /**
      * Returns the software version number for the device, for example,
      * the IMEI/SV for GSM phones. Return null if the software version is
      * not available.
@@ -3802,21 +3782,20 @@
     }
 
     /**
-     * Return if the current radio is LTE on CDMA. This is a tri-state return value as for a period
-     * of time the mode may be unknown.
+     * Return if the current radio has global mode enabled, meaning it supports
+     * both 3GPP and 3GPP2 radio technologies at the same time.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
-     * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
-     * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
-     *
+     * @return {@code true} if global mode is enabled
+     *         {@code false} if global mode is not enabled or unknown
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    @UnsupportedAppUsage
-    public int getLteOnCdmaMode() {
-        return getLteOnCdmaMode(getSubId());
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isGlobalModeEnabled() {
+        return getLteOnCdmaMode(getSubId()) == PhoneConstants.LTE_ON_CDMA_TRUE;
     }
 
     /**
@@ -3829,7 +3808,7 @@
      * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @UnsupportedAppUsage
     public int getLteOnCdmaMode(int subId) {
         try {
@@ -6042,9 +6021,11 @@
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @param p2 P2 parameter (described in ISO 7816-4).
      * @return an IccOpenLogicalChannelResponse object.
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.SEService#getUiccReader(int)},
+     *             {@link android.se.omapi.Reader#openSession()},
+     *             {@link android.se.omapi.Session#openLogicalChannel(byte[], byte)}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) {
         return iccOpenLogicalChannel(getSubId(), AID, p2);
@@ -6076,9 +6057,11 @@
      * @param p2 P2 parameter (described in ISO 7816-4).
      * @return an IccOpenLogicalChannelResponse object.
      * @hide
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.SEService#getUiccReader(int)},
+     *             {@link android.se.omapi.Reader#openSession()},
+     *             {@link android.se.omapi.Session#openLogicalChannel(byte[], byte)}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     public IccOpenLogicalChannelResponse iccOpenLogicalChannel(int subId, String AID, int p2) {
         try {
@@ -6107,9 +6090,9 @@
      *            iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
      * @hide
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.Channel#close()}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @SystemApi
@@ -6137,9 +6120,9 @@
      * @param channel is the channel id to be closed as returned by a successful
      *            iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.Channel#close()}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     public boolean iccCloseLogicalChannel(int channel) {
         return iccCloseLogicalChannel(getSubId(), channel);
@@ -6159,9 +6142,9 @@
      *            iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
      * @hide
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.Channel#close()}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     public boolean iccCloseLogicalChannel(int subId, int channel) {
         try {
@@ -6198,9 +6181,9 @@
      * @return The APDU response from the ICC card with the status appended at the end, or null if
      * there is an issue connecting to the Telephony service.
      * @hide
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.Channel#transmit(byte[])}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @SystemApi
@@ -6239,9 +6222,9 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.Channel#transmit(byte[])}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     public String iccTransmitApduLogicalChannel(int channel, int cla,
             int instruction, int p1, int p2, int p3, String data) {
@@ -6271,9 +6254,9 @@
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
      * @hide
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.Channel#transmit(byte[])}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     public String iccTransmitApduLogicalChannel(int subId, int channel, int cla,
             int instruction, int p1, int p2, int p3, String data) {
@@ -6310,9 +6293,12 @@
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
      * @hide
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.SEService#getUiccReader(int)},
+     *             {@link android.se.omapi.Reader#openSession()},
+     *             {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+     *             {@link android.se.omapi.Channel#transmit(byte[])}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @SystemApi
@@ -6349,9 +6335,12 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.SEService#getUiccReader(int)},
+     *             {@link android.se.omapi.Reader#openSession()},
+     *             {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+     *             {@link android.se.omapi.Channel#transmit(byte[])}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     public String iccTransmitApduBasicChannel(int cla,
             int instruction, int p1, int p2, int p3, String data) {
@@ -6379,9 +6368,12 @@
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
      * @hide
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.SEService#getUiccReader(int)},
+     *             {@link android.se.omapi.Reader#openSession()},
+     *             {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+     *             {@link android.se.omapi.Channel#transmit(byte[])}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     public String iccTransmitApduBasicChannel(int subId, int cla,
             int instruction, int p1, int p2, int p3, String data) {
@@ -6410,9 +6402,12 @@
      * @param p3 P3 value of the APDU command.
      * @param filePath
      * @return The APDU response.
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.SEService#getUiccReader(int)},
+     *             {@link android.se.omapi.Reader#openSession()},
+     *             {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+     *             {@link android.se.omapi.Channel#transmit(byte[])}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
             String filePath) {
@@ -6435,9 +6430,12 @@
      * @param filePath
      * @return The APDU response.
      * @hide
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.SEService#getUiccReader(int)},
+     *             {@link android.se.omapi.Reader#openSession()},
+     *             {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+     *             {@link android.se.omapi.Channel#transmit(byte[])}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     public byte[] iccExchangeSimIO(int subId, int fileID, int command, int p1, int p2,
             int p3, String filePath) {
@@ -6464,9 +6462,12 @@
      * @return The APDU response from the ICC card in hexadecimal format
      *         with the last 4 bytes being the status word. If the command fails,
      *         returns an empty string.
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.SEService#getUiccReader(int)},
+     *             {@link android.se.omapi.Reader#openSession()},
+     *             {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+     *             {@link android.se.omapi.Channel#transmit(byte[])}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     public String sendEnvelopeWithStatus(String content) {
         return sendEnvelopeWithStatus(getSubId(), content);
@@ -6487,9 +6488,12 @@
      *         with the last 4 bytes being the status word. If the command fails,
      *         returns an empty string.
      * @hide
-     * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+     * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+     *             {@link android.se.omapi.SEService#getUiccReader(int)},
+     *             {@link android.se.omapi.Reader#openSession()},
+     *             {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+     *             {@link android.se.omapi.Channel#transmit(byte[])}.
      */
-    // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
     @Deprecated
     public String sendEnvelopeWithStatus(int subId, String content) {
         try {
@@ -10840,28 +10844,6 @@
     }
 
     /**
-     * Get aggregated video call data usage since boot.
-     * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
-     *
-     * @param how one of the NetworkStats.STATS_PER_* constants depending on whether the request is
-     * for data usage per uid or overall usage.
-     * @return Snapshot of video call data usage
-     * @hide
-     */
-    public NetworkStats getVtDataUsage(int how) {
-        boolean perUidStats = (how == NetworkStats.STATS_PER_UID);
-        try {
-            ITelephony service = getITelephony();
-            if (service != null) {
-                return service.getVtDataUsage(getSubId(), perUidStats);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#getVtDataUsage", e);
-        }
-        return null;
-    }
-
-    /**
      * Policy control of data connection. Usually used when data limit is passed.
      * @param enabled True if enabling the data, otherwise disabling.
      * @param subId sub id
@@ -11623,6 +11605,7 @@
      * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
      * as the list of {@link EmergencyNumber}; empty Map if this information is not available;
      * or throw a SecurityException if the caller does not have the permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @NonNull
@@ -11670,6 +11653,7 @@
      * @param number - the number to look up
      * @return {@code true} if the given number is an emergency number based on current locale,
      * SIM card(s), Android database, modem, network or defaults; {@code false} otherwise.
+     * @throws IllegalStateException if the Telephony process is not currently available.
      */
     public boolean isEmergencyNumber(@NonNull String number) {
         try {
@@ -11705,7 +11689,7 @@
      * the same digits of any current emergency number based on current locale, sim, modem and
      * network; {@code false} if it is not; or throw an SecurityException if the caller does not
      * have the required permission/privileges
-     *
+     * @throws IllegalStateException if the Telephony process is not currently available.
      * @hide
      */
     @SystemApi
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
old mode 100644
new mode 100755
index 1b583fd..80c38cb
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -16,6 +16,8 @@
 
 package android.telephony.ims;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.CallQuality;
@@ -451,6 +453,21 @@
         }
 
         /**
+         * Received success response for call transfer request.
+         */
+        public void callSessionTransferred(@NonNull ImsCallSession session) {
+            // no-op
+        }
+
+        /**
+         * Received failure response for call transfer request.
+         */
+        public void callSessionTransferFailed(@NonNull ImsCallSession session,
+                @Nullable ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
          * Called when the IMS service reports a change to the call quality.
          */
         public void callQualityChanged(CallQuality callQuality) {
@@ -795,6 +812,41 @@
     }
 
     /**
+     * Transfers an ongoing call.
+     *
+     * @param number number to be transferred to.
+     * @param isConfirmationRequired indicates blind or assured transfer.
+     */
+    public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.transfer(number, isConfirmationRequired);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Transfers a call to another ongoing call.
+     *
+     * @param transferToSession the other ImsCallSession to which this session will be transferred.
+     */
+    public void transfer(@NonNull ImsCallSession transferToSession) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            if (transferToSession != null) {
+                miSession.consultativeTransfer(transferToSession.getSession());
+            }
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Terminates a call.
      *
      * @see Listener#callSessionTerminated
@@ -1410,6 +1462,20 @@
             }
         }
 
+        @Override
+        public void callSessionTransferred() {
+            if (mListener != null) {
+                mListener.callSessionTransferred(ImsCallSession.this);
+            }
+        }
+
+        @Override
+        public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
         /**
          * Call quality updated
          */
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
deleted file mode 100644
index e90548a..0000000
--- a/telephony/java/android/telephony/ims/Rcs1To1Thread.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.WorkerThread;
-
-/**
- * Rcs1To1Thread represents a single RCS conversation thread with a total of two
- * {@link RcsParticipant}s. Please see Section 5 (1-to-1 Messaging) - GSMA RCC.71 (RCS Universal
- * Profile Service Definition Document)
- *
- * @hide
- */
-public class Rcs1To1Thread extends RcsThread {
-    private int mThreadId;
-
-    /**
-     * Public constructor only for RcsMessageStoreController to initialize new threads.
-     *
-     * @hide
-     */
-    public Rcs1To1Thread(RcsControllerCall rcsControllerCall, int threadId) {
-        super(rcsControllerCall, threadId);
-        mThreadId = threadId;
-    }
-
-    /**
-     * @return Returns {@code false} as this is always a 1 to 1 thread.
-     */
-    @Override
-    public boolean isGroup() {
-        return false;
-    }
-
-    /**
-     * {@link Rcs1To1Thread}s can fall back to SMS as a back-up protocol. This function returns the
-     * thread id to be used to query {@code content://mms-sms/conversation/#} to get the fallback
-     * thread.
-     *
-     * @return The thread id to be used to query the mms-sms authority
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public long getFallbackThreadId() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.get1To1ThreadFallbackThreadId(mThreadId,
-                        callingPackage));
-    }
-
-    /**
-     * If the RCS client allows falling back to SMS, it needs to create an MMS-SMS thread in the
-     * SMS/MMS Provider( see {@link android.provider.Telephony.MmsSms#CONTENT_CONVERSATIONS_URI}.
-     * Use this function to link the {@link Rcs1To1Thread} to the MMS-SMS thread. This function
-     * also updates the storage.
-     *
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setFallbackThreadId(long fallbackThreadId) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.set1To1ThreadFallbackThreadId(mThreadId,
-                        fallbackThreadId, callingPackage));
-    }
-
-    /**
-     * @return Returns the {@link RcsParticipant} that receives the messages sent in this thread.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @NonNull
-    @WorkerThread
-    public RcsParticipant getRecipient() throws RcsMessageStoreException {
-        return new RcsParticipant(
-                mRcsControllerCall,
-                mRcsControllerCall.call(
-                        (iRcs, callingPackage) -> iRcs.get1To1ThreadOtherParticipantId(mThreadId,
-                                callingPackage)));
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsControllerCall.java b/telephony/java/android/telephony/ims/RcsControllerCall.java
deleted file mode 100644
index ce03c3c..0000000
--- a/telephony/java/android/telephony/ims/RcsControllerCall.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.ims.aidl.IRcsMessage;
-
-/**
- * A wrapper class around RPC calls that {@link RcsMessageManager} APIs to minimize boilerplate
- * code.
- *
- * @hide - not meant for public use
- */
-class RcsControllerCall {
-    private final Context mContext;
-
-    RcsControllerCall(Context context) {
-        mContext = context;
-    }
-
-    <R> R call(RcsServiceCall<R> serviceCall) throws RcsMessageStoreException {
-        IRcsMessage iRcsMessage = IRcsMessage.Stub.asInterface(ServiceManager.getService(
-                Context.TELEPHONY_RCS_MESSAGE_SERVICE));
-        if (iRcsMessage == null) {
-            throw new RcsMessageStoreException("Could not connect to RCS storage service");
-        }
-
-        try {
-            return serviceCall.methodOnIRcs(iRcsMessage, mContext.getOpPackageName());
-        } catch (RemoteException exception) {
-            throw new RcsMessageStoreException(exception.getMessage());
-        }
-    }
-
-    void callWithNoReturn(RcsServiceCallWithNoReturn serviceCall)
-            throws RcsMessageStoreException {
-        call((iRcsMessage, callingPackage) -> {
-            serviceCall.methodOnIRcs(iRcsMessage, callingPackage);
-            return null;
-        });
-    }
-
-    interface RcsServiceCall<R> {
-        R methodOnIRcs(IRcsMessage iRcs, String callingPackage) throws RemoteException;
-    }
-
-    interface RcsServiceCallWithNoReturn {
-        void methodOnIRcs(IRcsMessage iRcs, String callingPackage) throws RemoteException;
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsEvent.java b/telephony/java/android/telephony/ims/RcsEvent.java
deleted file mode 100644
index 9dd0720..0000000
--- a/telephony/java/android/telephony/ims/RcsEvent.java
+++ /dev/null
@@ -1,44 +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.telephony.ims;
-
-/**
- * The base class for events that can happen on {@link RcsParticipant}s and {@link RcsThread}s.
- *
- * @hide
- */
-public abstract class RcsEvent {
-    private final long mTimestamp;
-
-    protected RcsEvent(long timestamp) {
-        mTimestamp = timestamp;
-    }
-
-    /**
-     * @return Returns the time of when this event happened. The timestamp is defined as
-     * milliseconds passed after midnight, January 1, 1970 UTC
-     */
-    public long getTimestamp() {
-        return mTimestamp;
-    }
-
-    /**
-     * Persists the event to the data store
-     *
-     * @hide
-     */
-    abstract void persist(RcsControllerCall rcsControllerCall) throws RcsMessageStoreException;
-}
diff --git a/telephony/java/android/telephony/ims/RcsEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsEventDescriptor.aidl
deleted file mode 100644
index ab1c55e..0000000
--- a/telephony/java/android/telephony/ims/RcsEventDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsEventDescriptor.java b/telephony/java/android/telephony/ims/RcsEventDescriptor.java
deleted file mode 100644
index b44adea..0000000
--- a/telephony/java/android/telephony/ims/RcsEventDescriptor.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public abstract class RcsEventDescriptor implements Parcelable {
-    protected final long mTimestamp;
-
-    RcsEventDescriptor(long timestamp) {
-        mTimestamp = timestamp;
-    }
-
-    /**
-     * Creates an RcsEvent based on this RcsEventDescriptor. Overriding this method practically
-     * allows an injection point for RcsEvent dependencies outside of the values contained in the
-     * descriptor.
-     */
-    @VisibleForTesting(visibility = PROTECTED)
-    public abstract RcsEvent createRcsEvent(RcsControllerCall rcsControllerCall);
-
-    RcsEventDescriptor(Parcel in) {
-        mTimestamp = in.readLong();
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeLong(mTimestamp);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl b/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl
deleted file mode 100644
index f18c4df..0000000
--- a/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsEventQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryParams.java b/telephony/java/android/telephony/ims/RcsEventQueryParams.java
deleted file mode 100644
index 0024cf7..0000000
--- a/telephony/java/android/telephony/ims/RcsEventQueryParams.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import static android.provider.Telephony.RcsColumns.RcsEventTypes.ICON_CHANGED_EVENT_TYPE;
-import static android.provider.Telephony.RcsColumns.RcsEventTypes.NAME_CHANGED_EVENT_TYPE;
-import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE;
-import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_JOINED_EVENT_TYPE;
-import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_LEFT_EVENT_TYPE;
-
-import android.annotation.CheckResult;
-import android.annotation.IntDef;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.security.InvalidParameterException;
-
-/**
- * The parameters to pass into
- * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} in order to select a
- * subset of {@link RcsEvent}s present in the message store.
- *
- * @hide
- */
-public final class RcsEventQueryParams implements Parcelable {
-    /**
-     * Flag to be used with {@link Builder#setEventType(int)} to make
-     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return all types of
-     * {@link RcsEvent}s
-     */
-    public static final int ALL_EVENTS = -1;
-
-    /**
-     * Flag to be used with {@link Builder#setEventType(int)} to make
-     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return sub-types of
-     * {@link RcsGroupThreadEvent}s
-     */
-    public static final int ALL_GROUP_THREAD_EVENTS = 0;
-
-    /**
-     * Flag to be used with {@link Builder#setEventType(int)} to make
-     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
-     * {@link RcsParticipantAliasChangedEvent}s
-     */
-    public static final int PARTICIPANT_ALIAS_CHANGED_EVENT =
-            PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE;
-
-    /**
-     * Flag to be used with {@link Builder#setEventType(int)} to make
-     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
-     * {@link RcsGroupThreadParticipantJoinedEvent}s
-     */
-    public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT =
-            PARTICIPANT_JOINED_EVENT_TYPE;
-
-    /**
-     * Flag to be used with {@link Builder#setEventType(int)} to make
-     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
-     * {@link RcsGroupThreadParticipantLeftEvent}s
-     */
-    public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT =
-            PARTICIPANT_LEFT_EVENT_TYPE;
-
-    /**
-     * Flag to be used with {@link Builder#setEventType(int)} to make
-     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
-     * {@link RcsGroupThreadNameChangedEvent}s
-     */
-    public static final int GROUP_THREAD_NAME_CHANGED_EVENT = NAME_CHANGED_EVENT_TYPE;
-
-    /**
-     * Flag to be used with {@link Builder#setEventType(int)} to make
-     * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
-     * {@link RcsGroupThreadIconChangedEvent}s
-     */
-    public static final int GROUP_THREAD_ICON_CHANGED_EVENT = ICON_CHANGED_EVENT_TYPE;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({ALL_EVENTS, ALL_GROUP_THREAD_EVENTS, PARTICIPANT_ALIAS_CHANGED_EVENT,
-            GROUP_THREAD_PARTICIPANT_JOINED_EVENT, GROUP_THREAD_PARTICIPANT_LEFT_EVENT,
-            GROUP_THREAD_NAME_CHANGED_EVENT, GROUP_THREAD_ICON_CHANGED_EVENT})
-    public @interface EventType {
-    }
-
-    /**
-     * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted
-     * in the order of creation for faster query results.
-     */
-    public static final int SORT_BY_CREATION_ORDER = 0;
-
-    /**
-     * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted
-     * with respect to {@link RcsEvent#getTimestamp()}
-     */
-    public static final int SORT_BY_TIMESTAMP = 1;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
-    public @interface SortingProperty {
-    }
-
-    /**
-     * The key to pass into a Bundle, for usage in RcsProvider.query(Bundle)
-     * @hide - not meant for public use
-     */
-    public static final String EVENT_QUERY_PARAMETERS_KEY = "event_query_parameters";
-
-    // Which types of events the results should be limited to
-    private @EventType int mEventType;
-    // The property which the results should be sorted against
-    private int mSortingProperty;
-    // Whether the results should be sorted in ascending order
-    private boolean mIsAscending;
-    // The number of results that should be returned with this query
-    private int mLimit;
-    // The thread that the results are limited to
-    private int mThreadId;
-
-    RcsEventQueryParams(@EventType int eventType, int threadId,
-            @SortingProperty int sortingProperty, boolean isAscending, int limit) {
-        mEventType = eventType;
-        mSortingProperty = sortingProperty;
-        mIsAscending = isAscending;
-        mLimit = limit;
-        mThreadId = threadId;
-    }
-
-    /**
-     * @return Returns the type of {@link RcsEvent}s that this {@link RcsEventQueryParams} is
-     * set to query for.
-     */
-    public @EventType int getEventType() {
-        return mEventType;
-    }
-
-    /**
-     * @return Returns the number of {@link RcsEvent}s to be returned from the query. A value of
-     * 0 means there is no set limit.
-     */
-    public int getLimit() {
-        return mLimit;
-    }
-
-    /**
-     * @return Returns the property where the results should be sorted against.
-     * @see SortingProperty
-     */
-    public int getSortingProperty() {
-        return mSortingProperty;
-    }
-
-    /**
-     * @return Returns {@code true} if the result set will be sorted in ascending order,
-     * {@code false} if it will be sorted in descending order.
-     */
-    public boolean getSortDirection() {
-        return mIsAscending;
-    }
-
-    /**
-     * @return Returns the ID of the {@link RcsGroupThread} that the results are limited to. As this
-     * API exposes an ID, it should stay hidden.
-     *
-     * @hide
-     */
-    public int getThreadId() {
-        return mThreadId;
-    }
-
-    /**
-     * A helper class to build the {@link RcsEventQueryParams}.
-     */
-    public static class Builder {
-        private @EventType int mEventType;
-        private @SortingProperty int mSortingProperty;
-        private boolean mIsAscending;
-        private int mLimit = 100;
-        private int mThreadId;
-
-        /**
-         * Creates a new builder for {@link RcsEventQueryParams} to be used in
-         * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
-         */
-        public Builder() {
-            // empty implementation
-        }
-
-        /**
-         * Desired number of events to be returned from the query. Passing in 0 will return all
-         * existing events at once. The limit defaults to 100.
-         *
-         * @param limit The number to limit the query result to.
-         * @return The same instance of the builder to chain parameters.
-         * @throws InvalidParameterException If the given limit is negative.
-         */
-        @CheckResult
-        public Builder setResultLimit(@IntRange(from = 0) int limit)
-                throws InvalidParameterException {
-            if (limit < 0) {
-                throw new InvalidParameterException("The query limit must be non-negative");
-            }
-
-            mLimit = limit;
-            return this;
-        }
-
-        /**
-         * Sets the type of events to be returned from the query.
-         *
-         * @param eventType The type of event to be returned.
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setEventType(@EventType int eventType) {
-            mEventType = eventType;
-            return this;
-        }
-
-        /**
-         * Sets the property where the results should be sorted against. Defaults to
-         * {@link RcsEventQueryParams.SortingProperty#SORT_BY_CREATION_ORDER}
-         *
-         * @param sortingProperty against which property the results should be sorted
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setSortProperty(@SortingProperty int sortingProperty) {
-            mSortingProperty = sortingProperty;
-            return this;
-        }
-
-        /**
-         * Sets whether the results should be sorted ascending or descending
-         *
-         * @param isAscending whether the results should be sorted ascending
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setSortDirection(boolean isAscending) {
-            mIsAscending = isAscending;
-            return this;
-        }
-
-        /**
-         * Limits the results to the given {@link RcsGroupThread}. Setting this value prevents
-         * returning any instances of {@link RcsParticipantAliasChangedEvent}.
-         *
-         * @param groupThread The thread to limit the results to.
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setGroupThread(@NonNull RcsGroupThread groupThread) {
-            mThreadId = groupThread.getThreadId();
-            return this;
-        }
-
-        /**
-         * Builds the {@link RcsEventQueryParams} to use in
-         * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
-         *
-         * @return An instance of {@link RcsEventQueryParams} to use with the event query.
-         */
-        public RcsEventQueryParams build() {
-            return new RcsEventQueryParams(mEventType, mThreadId, mSortingProperty,
-                    mIsAscending, mLimit);
-        }
-    }
-
-    private RcsEventQueryParams(Parcel in) {
-        mEventType = in.readInt();
-        mThreadId = in.readInt();
-        mSortingProperty = in.readInt();
-        mIsAscending = in.readBoolean();
-        mLimit = in.readInt();
-    }
-
-    public static final @android.annotation.NonNull Creator<RcsEventQueryParams> CREATOR =
-            new Creator<RcsEventQueryParams>() {
-                @Override
-                public RcsEventQueryParams createFromParcel(Parcel in) {
-                    return new RcsEventQueryParams(in);
-                }
-
-                @Override
-                public RcsEventQueryParams[] newArray(int size) {
-                    return new RcsEventQueryParams[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mEventType);
-        dest.writeInt(mThreadId);
-        dest.writeInt(mSortingProperty);
-        dest.writeBoolean(mIsAscending);
-        dest.writeInt(mLimit);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResult.java b/telephony/java/android/telephony/ims/RcsEventQueryResult.java
deleted file mode 100644
index d6347e3..0000000
--- a/telephony/java/android/telephony/ims/RcsEventQueryResult.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import java.util.List;
-
-/**
- * The result of a {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
- * call. This class allows getting the token for querying the next batch of events in order to
- * prevent handling large amounts of data at once.
- *
- * @hide
- */
-public class RcsEventQueryResult {
-    private RcsQueryContinuationToken mContinuationToken;
-    private List<RcsEvent> mEvents;
-
-    /**
-     * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
-     * to create query results
-     *
-     * @hide
-     */
-    public RcsEventQueryResult(
-            RcsQueryContinuationToken continuationToken,
-            List<RcsEvent> events) {
-        mContinuationToken = continuationToken;
-        mEvents = events;
-    }
-
-    /**
-     * Returns a token to call
-     * {@link RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)}
-     * to get the next batch of {@link RcsEvent}s.
-     */
-    public RcsQueryContinuationToken getContinuationToken() {
-        return mContinuationToken;
-    }
-
-    /**
-     * Returns all the {@link RcsEvent}s in the current query result. Call {@link
-     * RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)} to get the next batch
-     * of {@link RcsEvent}s.
-     */
-    public List<RcsEvent> getEvents() {
-        return mEvents;
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResultDescriptor.aidl b/telephony/java/android/telephony/ims/RcsEventQueryResultDescriptor.aidl
deleted file mode 100644
index 0beaaab..0000000
--- a/telephony/java/android/telephony/ims/RcsEventQueryResultDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsEventQueryResultDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResultDescriptor.java b/telephony/java/android/telephony/ims/RcsEventQueryResultDescriptor.java
deleted file mode 100644
index b972d55..0000000
--- a/telephony/java/android/telephony/ims/RcsEventQueryResultDescriptor.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Contains the raw data backing a {@link RcsEventQueryResult}.
- *
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsEventQueryResultDescriptor implements Parcelable {
-    private final RcsQueryContinuationToken mContinuationToken;
-    private final List<RcsEventDescriptor> mEvents;
-
-    public RcsEventQueryResultDescriptor(
-            RcsQueryContinuationToken continuationToken,
-            List<RcsEventDescriptor> events) {
-        mContinuationToken = continuationToken;
-        mEvents = events;
-    }
-
-    protected RcsEventQueryResult getRcsEventQueryResult(RcsControllerCall rcsControllerCall) {
-        List<RcsEvent> rcsEvents = mEvents.stream()
-                .map(rcsEvent -> rcsEvent.createRcsEvent(rcsControllerCall))
-                .collect(Collectors.toList());
-
-        return new RcsEventQueryResult(mContinuationToken, rcsEvents);
-    }
-
-    protected RcsEventQueryResultDescriptor(Parcel in) {
-        mContinuationToken = in.readParcelable(RcsQueryContinuationToken.class.getClassLoader());
-        mEvents = new LinkedList<>();
-        in.readList(mEvents, null);
-    }
-
-    public static final @android.annotation.NonNull Creator<RcsEventQueryResultDescriptor> CREATOR =
-            new Creator<RcsEventQueryResultDescriptor>() {
-        @Override
-        public RcsEventQueryResultDescriptor createFromParcel(Parcel in) {
-            return new RcsEventQueryResultDescriptor(in);
-        }
-
-        @Override
-        public RcsEventQueryResultDescriptor[] newArray(int size) {
-            return new RcsEventQueryResultDescriptor[size];
-        }
-    };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(mContinuationToken, flags);
-        dest.writeList(mEvents);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl
deleted file mode 100644
index 1552190..0000000
--- a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsFileTransferCreationParams;
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
deleted file mode 100644
index e43552d..0000000
--- a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.CheckResult;
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Pass an instance of this class to
- * {@link RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)} create an
- * {@link RcsFileTransferPart} and save it into storage.
- *
- * @hide
- */
-public final class RcsFileTransferCreationParams implements Parcelable {
-    private String mRcsFileTransferSessionId;
-    private Uri mContentUri;
-    private String mContentMimeType;
-    private long mFileSize;
-    private long mTransferOffset;
-    private int mWidth;
-    private int mHeight;
-    private long mMediaDuration;
-    private Uri mPreviewUri;
-    private String mPreviewMimeType;
-    private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus;
-
-    /**
-     * @return Returns the globally unique RCS file transfer session ID for the
-     * {@link RcsFileTransferPart} to be created
-     */
-    public String getRcsFileTransferSessionId() {
-        return mRcsFileTransferSessionId;
-    }
-
-    /**
-     * @return Returns the URI for the content of the {@link RcsFileTransferPart} to be created
-     */
-    public Uri getContentUri() {
-        return mContentUri;
-    }
-
-    /**
-     * @return Returns the MIME type for the content of the {@link RcsFileTransferPart} to be
-     * created
-     */
-    public String getContentMimeType() {
-        return mContentMimeType;
-    }
-
-    /**
-     * @return Returns the file size in bytes for the {@link RcsFileTransferPart} to be created
-     */
-    public long getFileSize() {
-        return mFileSize;
-    }
-
-    /**
-     * @return Returns the transfer offset for the {@link RcsFileTransferPart} to be created. The
-     * file transfer offset is defined as how many bytes have been successfully transferred to the
-     * receiver of this file transfer.
-     */
-    public long getTransferOffset() {
-        return mTransferOffset;
-    }
-
-    /**
-     * @return Returns the width of the {@link RcsFileTransferPart} to be created. The value is in
-     * pixels.
-     */
-    public int getWidth() {
-        return mWidth;
-    }
-
-    /**
-     * @return Returns the height of the {@link RcsFileTransferPart} to be created. The value is in
-     * pixels.
-     */
-    public int getHeight() {
-        return mHeight;
-    }
-
-    /**
-     * @return Returns the duration of the {@link RcsFileTransferPart} to be created.
-     */
-    public long getMediaDuration() {
-        return mMediaDuration;
-    }
-
-    /**
-     * @return Returns the URI of the preview of the content of the {@link RcsFileTransferPart} to
-     * be created. This should only be used for multi-media files.
-     */
-    public Uri getPreviewUri() {
-        return mPreviewUri;
-    }
-
-    /**
-     * @return Returns the MIME type of the preview of the content of the
-     * {@link RcsFileTransferPart} to be created. This should only be used for multi-media files.
-     */
-    public String getPreviewMimeType() {
-        return mPreviewMimeType;
-    }
-
-    /**
-     * @return Returns the status of the {@link RcsFileTransferPart} to be created.
-     */
-    public @RcsFileTransferPart.RcsFileTransferStatus int getFileTransferStatus() {
-        return mFileTransferStatus;
-    }
-
-    /**
-     * @hide
-     */
-    RcsFileTransferCreationParams(Builder builder) {
-        mRcsFileTransferSessionId = builder.mRcsFileTransferSessionId;
-        mContentUri = builder.mContentUri;
-        mContentMimeType = builder.mContentMimeType;
-        mFileSize = builder.mFileSize;
-        mTransferOffset = builder.mTransferOffset;
-        mWidth = builder.mWidth;
-        mHeight = builder.mHeight;
-        mMediaDuration = builder.mLength;
-        mPreviewUri = builder.mPreviewUri;
-        mPreviewMimeType = builder.mPreviewMimeType;
-        mFileTransferStatus = builder.mFileTransferStatus;
-    }
-
-    /**
-     * A builder to create instances of {@link RcsFileTransferCreationParams}
-     */
-    public class Builder {
-        private String mRcsFileTransferSessionId;
-        private Uri mContentUri;
-        private String mContentMimeType;
-        private long mFileSize;
-        private long mTransferOffset;
-        private int mWidth;
-        private int mHeight;
-        private long mLength;
-        private Uri mPreviewUri;
-        private String mPreviewMimeType;
-        private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus;
-
-        /**
-         * Sets the globally unique RCS file transfer session ID for the {@link RcsFileTransferPart}
-         * to be created
-         *
-         * @param sessionId The RCS file transfer session ID
-         * @return The same instance of {@link Builder} to chain methods
-         */
-        @CheckResult
-        public Builder setFileTransferSessionId(String sessionId) {
-            mRcsFileTransferSessionId = sessionId;
-            return this;
-        }
-
-        /**
-         * Sets the URI for the content of the {@link RcsFileTransferPart} to be created
-         *
-         * @param contentUri The URI for the file
-         * @return The same instance of {@link Builder} to chain methods
-         */
-        @CheckResult
-        public Builder setContentUri(Uri contentUri) {
-            mContentUri = contentUri;
-            return this;
-        }
-
-        /**
-         * Sets the MIME type for the content of the {@link RcsFileTransferPart} to be created
-         *
-         * @param contentType The MIME type of the file
-         * @return The same instance of {@link Builder} to chain methods
-         */
-        @CheckResult
-        public Builder setContentMimeType(String contentType) {
-            mContentMimeType = contentType;
-            return this;
-        }
-
-        /**
-         * Sets the file size for the {@link RcsFileTransferPart} to be created
-         *
-         * @param size The size of the file in bytes
-         * @return The same instance of {@link Builder} to chain methods
-         */
-        @CheckResult
-        public Builder setFileSize(long size) {
-            mFileSize = size;
-            return this;
-        }
-
-        /**
-         * Sets the transfer offset for the {@link RcsFileTransferPart} to be created. The file
-         * transfer offset is defined as how many bytes have been successfully transferred to the
-         * receiver of this file transfer.
-         *
-         * @param offset The transfer offset in bytes
-         * @return The same instance of {@link Builder} to chain methods
-         */
-        @CheckResult
-        public Builder setTransferOffset(long offset) {
-            mTransferOffset = offset;
-            return this;
-        }
-
-        /**
-         * Sets the width of the {@link RcsFileTransferPart} to be created. This should only be used
-         * for multi-media files.
-         *
-         * @param width The width of the multi-media file in pixels.
-         * @return The same instance of {@link Builder} to chain methods
-         */
-        @CheckResult
-        public Builder setWidth(int width) {
-            mWidth = width;
-            return this;
-        }
-
-        /**
-         * Sets the height of the {@link RcsFileTransferPart} to be created. This should only be
-         * used for multi-media files.
-         *
-         * @param height The height of the multi-media file in pixels.
-         * @return The same instance of {@link Builder} to chain methods
-         */
-        @CheckResult
-        public Builder setHeight(int height) {
-            mHeight = height;
-            return this;
-        }
-
-        /**
-         * Sets the length of the {@link RcsFileTransferPart} to be created. This should only be
-         * used for multi-media files such as audio or video.
-         *
-         * @param length The length of the multi-media file in milliseconds
-         * @return The same instance of {@link Builder} to chain methods
-         */
-        @CheckResult
-        public Builder setMediaDuration(long length) {
-            mLength = length;
-            return this;
-        }
-
-        /**
-         * Sets the URI of the preview of the content of the {@link RcsFileTransferPart} to be
-         * created. This should only be used for multi-media files.
-         *
-         * @param previewUri The URI of the preview of the file transfer
-         * @return The same instance of {@link Builder} to chain methods
-         */
-        @CheckResult
-        public Builder setPreviewUri(Uri previewUri) {
-            mPreviewUri = previewUri;
-            return this;
-        }
-
-        /**
-         * Sets the MIME type of the preview of the content of the {@link RcsFileTransferPart} to
-         * be created. This should only be used for multi-media files.
-         *
-         * @param previewType The MIME type of the preview of the file transfer
-         * @return The same instance of {@link Builder} to chain methods
-         */
-        @CheckResult
-        public Builder setPreviewMimeType(String previewType) {
-            mPreviewMimeType = previewType;
-            return this;
-        }
-
-        /**
-         * Sets the status of the {@link RcsFileTransferPart} to be created.
-         *
-         * @param status The status of the file transfer
-         * @return The same instance of {@link Builder} to chain methods
-         */
-        @CheckResult
-        public Builder setFileTransferStatus(
-                @RcsFileTransferPart.RcsFileTransferStatus int status) {
-            mFileTransferStatus = status;
-            return this;
-        }
-
-        /**
-         * Creates an instance of {@link RcsFileTransferCreationParams} with the given
-         * parameters.
-         *
-         * @return The same instance of {@link Builder} to chain methods
-         * @see RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)
-         */
-        public RcsFileTransferCreationParams build() {
-            return new RcsFileTransferCreationParams(this);
-        }
-    }
-
-    private RcsFileTransferCreationParams(Parcel in) {
-        mRcsFileTransferSessionId = in.readString();
-        mContentUri = in.readParcelable(Uri.class.getClassLoader());
-        mContentMimeType = in.readString();
-        mFileSize = in.readLong();
-        mTransferOffset = in.readLong();
-        mWidth = in.readInt();
-        mHeight = in.readInt();
-        mMediaDuration = in.readLong();
-        mPreviewUri = in.readParcelable(Uri.class.getClassLoader());
-        mPreviewMimeType = in.readString();
-        mFileTransferStatus = in.readInt();
-    }
-
-    public static final @android.annotation.NonNull Creator<RcsFileTransferCreationParams> CREATOR =
-            new Creator<RcsFileTransferCreationParams>() {
-                @Override
-                public RcsFileTransferCreationParams createFromParcel(Parcel in) {
-                    return new RcsFileTransferCreationParams(in);
-                }
-
-                @Override
-                public RcsFileTransferCreationParams[] newArray(int size) {
-                    return new RcsFileTransferCreationParams[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mRcsFileTransferSessionId);
-        dest.writeParcelable(mContentUri, flags);
-        dest.writeString(mContentMimeType);
-        dest.writeLong(mFileSize);
-        dest.writeLong(mTransferOffset);
-        dest.writeInt(mWidth);
-        dest.writeInt(mHeight);
-        dest.writeLong(mMediaDuration);
-        dest.writeParcelable(mPreviewUri, flags);
-        dest.writeString(mPreviewMimeType);
-        dest.writeInt(mFileTransferStatus);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.java b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
deleted file mode 100644
index ef66a76..0000000
--- a/telephony/java/android/telephony/ims/RcsFileTransferPart.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.annotation.WorkerThread;
-import android.net.Uri;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A part of a composite {@link RcsMessage} that holds a file transfer. Please see Section 7
- * (File Transfer) - GSMA RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @hide
- */
-public class RcsFileTransferPart {
-    /**
-     * The status to indicate that this {@link RcsFileTransferPart} is not set yet.
-     */
-    public static final int NOT_SET = 0;
-
-    /**
-     * The status to indicate that this {@link RcsFileTransferPart} is a draft and is not in the
-     * process of sending yet.
-     */
-    public static final int DRAFT = 1;
-
-    /**
-     * The status to indicate that this {@link RcsFileTransferPart} is actively being sent right
-     * now.
-     */
-    public static final int SENDING = 2;
-
-    /**
-     * The status to indicate that this {@link RcsFileTransferPart} was being sent, but the user has
-     * paused the sending process.
-     */
-    public static final int SENDING_PAUSED = 3;
-
-    /**
-     * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to
-     * send.
-     */
-    public static final int SENDING_FAILED = 4;
-
-    /**
-     * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to
-     * send.
-     */
-    public static final int SENDING_CANCELLED = 5;
-
-    /**
-     * The status to indicate that this {@link RcsFileTransferPart} is actively being downloaded
-     * right now.
-     */
-    public static final int DOWNLOADING = 6;
-
-    /**
-     * The status to indicate that this {@link RcsFileTransferPart} was being downloaded, but the
-     * user paused the downloading process.
-     */
-    public static final int DOWNLOADING_PAUSED = 7;
-
-    /**
-     * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to
-     * download.
-     */
-    public static final int DOWNLOADING_FAILED = 8;
-
-    /**
-     * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to
-     * download.
-     */
-    public static final int DOWNLOADING_CANCELLED = 9;
-
-    /**
-     * The status to indicate that this {@link RcsFileTransferPart} was successfully sent or
-     * received.
-     */
-    public static final int SUCCEEDED = 10;
-
-    @IntDef({
-            DRAFT, SENDING, SENDING_PAUSED, SENDING_FAILED, SENDING_CANCELLED, DOWNLOADING,
-            DOWNLOADING_PAUSED, DOWNLOADING_FAILED, DOWNLOADING_CANCELLED, SUCCEEDED
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RcsFileTransferStatus {
-    }
-
-    private final RcsControllerCall mRcsControllerCall;
-
-    private int mId;
-
-    /**
-     * @hide
-     */
-    RcsFileTransferPart(RcsControllerCall rcsControllerCall, int id) {
-        mRcsControllerCall = rcsControllerCall;
-        mId = id;
-    }
-
-    /**
-     * @hide
-     */
-    public void setId(int id) {
-        mId = id;
-    }
-
-    /**
-     * @hide
-     */
-    public int getId() {
-        return mId;
-    }
-
-    /**
-     * Sets the RCS file transfer session ID for this file transfer and persists into storage.
-     *
-     * @param sessionId The session ID to be used for this file transfer.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setFileTransferSessionId(String sessionId) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setFileTransferSessionId(mId, sessionId,
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns the file transfer session ID.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public String getFileTransferSessionId() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getFileTransferSessionId(mId, callingPackage));
-    }
-
-    /**
-     * Sets the content URI for this file transfer and persists into storage. The file transfer
-     * should be reachable using this URI.
-     *
-     * @param contentUri The URI for this file transfer.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setContentUri(Uri contentUri) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setFileTransferContentUri(mId, contentUri,
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns the URI for this file transfer
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @Nullable
-    @WorkerThread
-    public Uri getContentUri() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getFileTransferContentUri(mId, callingPackage));
-    }
-
-    /**
-     * Sets the MIME type of this file transfer and persists into storage. Whether this type
-     * actually matches any known or supported types is not checked.
-     *
-     * @param contentMimeType The type of this file transfer.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setContentMimeType(String contentMimeType) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setFileTransferContentType(mId, contentMimeType,
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns the content type of this file transfer
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    @Nullable
-    public String getContentMimeType() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getFileTransferContentType(mId, callingPackage));
-    }
-
-    /**
-     * Sets the content length (i.e. file size) for this file transfer and persists into storage.
-     *
-     * @param contentLength The content length of this file transfer
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setFileSize(long contentLength) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setFileTransferFileSize(mId, contentLength,
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns the content length (i.e. file size) for this file transfer.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public long getFileSize() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getFileTransferFileSize(mId, callingPackage));
-    }
-
-    /**
-     * Sets the transfer offset for this file transfer and persists into storage. The file transfer
-     * offset is defined as how many bytes have been successfully transferred to the receiver of
-     * this file transfer.
-     *
-     * @param transferOffset The transfer offset for this file transfer.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setTransferOffset(long transferOffset) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setFileTransferTransferOffset(mId, transferOffset,
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns the number of bytes that have successfully transferred.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public long getTransferOffset() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getFileTransferTransferOffset(mId, callingPackage));
-    }
-
-    /**
-     * Sets the status for this file transfer and persists into storage.
-     *
-     * @param status The status of this file transfer.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setFileTransferStatus(@RcsFileTransferStatus int status)
-            throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setFileTransferStatus(mId, status, callingPackage));
-    }
-
-    /**
-     * @return Returns the status of this file transfer.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public @RcsFileTransferStatus int getFileTransferStatus() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getFileTransferStatus(mId, callingPackage));
-    }
-
-    /**
-     * @return Returns the width of this multi-media message part in pixels.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public int getWidth() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getFileTransferWidth(mId, callingPackage));
-    }
-
-    /**
-     * Sets the width of this RCS multi-media message part and persists into storage.
-     *
-     * @param width The width value in pixels
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setWidth(int width) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setFileTransferWidth(mId, width, callingPackage));
-    }
-
-    /**
-     * @return Returns the height of this multi-media message part in pixels.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public int getHeight() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getFileTransferHeight(mId, callingPackage));
-    }
-
-    /**
-     * Sets the height of this RCS multi-media message part and persists into storage.
-     *
-     * @param height The height value in pixels
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setHeight(int height) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setFileTransferHeight(mId, height, callingPackage));
-    }
-
-    /**
-     * @return Returns the length of this multi-media file (e.g. video or audio) in milliseconds.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public long getLength() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getFileTransferLength(mId, callingPackage));
-    }
-
-    /**
-     * Sets the length of this multi-media file (e.g. video or audio) and persists into storage.
-     *
-     * @param length The length of the file in milliseconds.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setLength(long length) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setFileTransferLength(mId, length, callingPackage));
-    }
-
-    /**
-     * @return Returns the URI for the preview of this multi-media file (e.g. an image thumbnail for
-     * a video)
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public Uri getPreviewUri() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getFileTransferPreviewUri(mId, callingPackage));
-    }
-
-    /**
-     * Sets the URI for the preview of this multi-media file and persists into storage.
-     *
-     * @param previewUri The URI to access to the preview file.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setPreviewUri(Uri previewUri) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setFileTransferPreviewUri(mId, previewUri,
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns the MIME type of this multi-media file's preview.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public String getPreviewMimeType() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getFileTransferPreviewType(mId, callingPackage));
-    }
-
-    /**
-     * Sets the MIME type for this multi-media file's preview and persists into storage.
-     *
-     * @param previewMimeType The MIME type for the preview
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setPreviewMimeType(String previewMimeType) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setFileTransferPreviewType(mId, previewMimeType,
-                        callingPackage));
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java
deleted file mode 100644
index 30abcb4..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThread.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.WorkerThread;
-import android.net.Uri;
-
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join
- * or leave. Please see Section 6 (Group Chat) - GSMA RCC.71 (RCS Universal Profile Service
- * Definition Document)
- *
- * @hide
- */
-public class RcsGroupThread extends RcsThread {
-    /**
-     * Public constructor only for RcsMessageStoreController to initialize new threads.
-     *
-     * @hide
-     */
-    public RcsGroupThread(RcsControllerCall rcsControllerCall, int threadId) {
-        super(rcsControllerCall, threadId);
-    }
-
-    /**
-     * @return Returns {@code true} as this is always a group thread
-     */
-    @Override
-    public boolean isGroup() {
-        return true;
-    }
-
-    /**
-     * @return Returns the given name of this {@link RcsGroupThread}. Please see US6-2 - GSMA RCC.71
-     * (RCS Universal Profile Service Definition Document)
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @Nullable
-    @WorkerThread
-    public String getGroupName() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getGroupThreadName(mThreadId, callingPackage));
-    }
-
-    /**
-     * Sets the name of this {@link RcsGroupThread} and saves it into storage. Please see US6-2 -
-     * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
-     *
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setGroupName(String groupName) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setGroupThreadName(mThreadId, groupName,
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns a URI that points to the group's icon {@link RcsGroupThread}. Please see
-     * US6-2 - GSMA RCC.71 (RCS Universal Profile Service Definition Document)
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @Nullable
-    public Uri getGroupIcon() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getGroupThreadIcon(mThreadId, callingPackage));
-    }
-
-    /**
-     * Sets the icon for this {@link RcsGroupThread} and saves it into storage. Please see US6-2 -
-     * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
-     *
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setGroupIcon(@Nullable Uri groupIcon) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setGroupThreadIcon(mThreadId, groupIcon,
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns the owner of this thread or {@code null} if there doesn't exist an owner
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @Nullable
-    @WorkerThread
-    public RcsParticipant getOwner() throws RcsMessageStoreException {
-        return new RcsParticipant(
-                mRcsControllerCall,
-                mRcsControllerCall.call(
-                        (iRcs, callingPackage) -> iRcs.getGroupThreadOwner(mThreadId,
-                                callingPackage)));
-    }
-
-    /**
-     * Sets the owner of this {@link RcsGroupThread} and saves it into storage. This is intended to
-     * be used for selecting a new owner for a group thread if the owner leaves the thread. The
-     * owner needs to be in the list of existing participants.
-     *
-     * @param participant The new owner of the thread. {@code null} values are allowed.
-     * @throws RcsMessageStoreException if the operation could not be persisted into storage
-     */
-    @WorkerThread
-    public void setOwner(@Nullable RcsParticipant participant) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setGroupThreadOwner(mThreadId, participant.getId(),
-                        callingPackage));
-    }
-
-    /**
-     * Adds a new {@link RcsParticipant} to this group thread and persists into storage. If the user
-     * is actively participating in this {@link RcsGroupThread}, an {@link RcsParticipant} on behalf
-     * of them should be added.
-     *
-     * @param participant The new participant to be added to the thread.
-     * @throws RcsMessageStoreException if the operation could not be persisted into storage
-     */
-    @WorkerThread
-    public void addParticipant(@NonNull RcsParticipant participant)
-            throws RcsMessageStoreException {
-        if (participant == null) {
-            return;
-        }
-
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.addParticipantToGroupThread(mThreadId,
-                        participant.getId(), callingPackage));
-    }
-
-    /**
-     * Removes an {@link RcsParticipant} from this group thread and persists into storage. If the
-     * removed participant was the owner of this group, the owner will become null.
-     *
-     * @throws RcsMessageStoreException if the operation could not be persisted into storage
-     */
-    @WorkerThread
-    public void removeParticipant(@NonNull RcsParticipant participant)
-            throws RcsMessageStoreException {
-        if (participant == null) {
-            return;
-        }
-
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.removeParticipantFromGroupThread(mThreadId,
-                        participant.getId(), callingPackage));
-    }
-
-    /**
-     * Returns the set of {@link RcsParticipant}s that contribute to this group thread. The
-     * returned set does not support modifications, please use
-     * {@link RcsGroupThread#addParticipant(RcsParticipant)}
-     * and {@link RcsGroupThread#removeParticipant(RcsParticipant)} instead.
-     *
-     * @return the immutable set of {@link RcsParticipant} in this group thread.
-     * @throws RcsMessageStoreException if the values could not be read from the storage
-     */
-    @WorkerThread
-    @NonNull
-    public Set<RcsParticipant> getParticipants() throws RcsMessageStoreException {
-        RcsParticipantQueryParams queryParameters =
-                new RcsParticipantQueryParams.Builder().setThread(this).build();
-
-        RcsParticipantQueryResult queryResult = new RcsParticipantQueryResult(
-                mRcsControllerCall,
-                mRcsControllerCall.call(
-                        (iRcs, callingPackage) -> iRcs.getParticipants(queryParameters,
-                                callingPackage)));
-
-        List<RcsParticipant> participantList = queryResult.getParticipants();
-        Set<RcsParticipant> participantSet = new LinkedHashSet<>(participantList);
-        return Collections.unmodifiableSet(participantSet);
-    }
-
-    /**
-     * Returns the conference URI for this {@link RcsGroupThread}. Please see 4.4.5.2 - GSMA RCC.53
-     * (RCS Device API 1.6 Specification
-     *
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @Nullable
-    @WorkerThread
-    public Uri getConferenceUri() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getGroupThreadConferenceUri(mThreadId,
-                        callingPackage));
-    }
-
-    /**
-     * Sets the conference URI for this {@link RcsGroupThread} and persists into storage. Please see
-     * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
-     *
-     * @param conferenceUri The URI as String to be used as the conference URI.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @Nullable
-    @WorkerThread
-    public void setConferenceUri(Uri conferenceUri) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setGroupThreadConferenceUri(mThreadId, conferenceUri,
-                        callingPackage));
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
deleted file mode 100644
index f4beef7f..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
+++ /dev/null
@@ -1,51 +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.telephony.ims;
-
-import android.annotation.NonNull;
-
-/**
- * An event that happened on an {@link RcsGroupThread}.
- *
- * @hide
- */
-public abstract class RcsGroupThreadEvent extends RcsEvent {
-    private final RcsGroupThread mRcsGroupThread;
-    private final RcsParticipant mOriginatingParticipant;
-
-    RcsGroupThreadEvent(long timestamp, RcsGroupThread rcsGroupThread,
-            RcsParticipant originatingParticipant) {
-        super(timestamp);
-        mRcsGroupThread = rcsGroupThread;
-        mOriginatingParticipant = originatingParticipant;
-    }
-
-    /**
-     * @return Returns the {@link RcsGroupThread} that this event happened on.
-     */
-    @NonNull
-    public RcsGroupThread getRcsGroupThread() {
-        return mRcsGroupThread;
-    }
-
-    /**
-     * @return Returns the {@link RcsParticipant} that performed the event.
-     */
-    @NonNull
-    public RcsParticipant getOriginatingParticipant() {
-        return mOriginatingParticipant;
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadEventDescriptor.aidl
deleted file mode 100644
index 6299d8a..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadEventDescriptor.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.telephony.ims;
-
-parcelable RcsGroupThreadEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEventDescriptor.java b/telephony/java/android/telephony/ims/RcsGroupThreadEventDescriptor.java
deleted file mode 100644
index 662a264..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadEventDescriptor.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public abstract class RcsGroupThreadEventDescriptor extends RcsEventDescriptor {
-    protected final int mRcsGroupThreadId;
-    protected final int mOriginatingParticipantId;
-
-    RcsGroupThreadEventDescriptor(long timestamp, int rcsGroupThreadId,
-            int originatingParticipantId) {
-        super(timestamp);
-        mRcsGroupThreadId = rcsGroupThreadId;
-        mOriginatingParticipantId = originatingParticipantId;
-    }
-
-    RcsGroupThreadEventDescriptor(Parcel in) {
-        super(in);
-        mRcsGroupThreadId = in.readInt();
-        mOriginatingParticipantId = in.readInt();
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeInt(mRcsGroupThreadId);
-        dest.writeInt(mOriginatingParticipantId);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
deleted file mode 100644
index 23e39ff..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
+++ /dev/null
@@ -1,71 +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.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.Uri;
-
-/**
- * An event that indicates an {@link RcsGroupThread}'s icon was changed. Please see R6-2-5 - GSMA
- * RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @hide
- */
-public final class RcsGroupThreadIconChangedEvent extends RcsGroupThreadEvent {
-    private final Uri mNewIcon;
-
-    /**
-     * Creates a new {@link RcsGroupThreadIconChangedEvent}. This event is not persisted into
-     * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
-     *
-     * @param timestamp The timestamp of when this event happened, in milliseconds passed after
-     *                  midnight, January 1st, 1970 UTC
-     * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
-     * @param originatingParticipant The {@link RcsParticipant} that changed the
-     *                               {@link RcsGroupThread}'s icon.
-     * @param newIcon {@link Uri} to the new icon of this {@link RcsGroupThread}
-     * @see RcsMessageStore#persistRcsEvent(RcsEvent)
-     */
-    public RcsGroupThreadIconChangedEvent(long timestamp,
-            @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant,
-            @Nullable Uri newIcon) {
-        super(timestamp, rcsGroupThread, originatingParticipant);
-        mNewIcon = newIcon;
-    }
-
-    /**
-     * @return Returns the {@link Uri} to the icon of the {@link RcsGroupThread} after this
-     * {@link RcsGroupThreadIconChangedEvent} occured.
-     */
-    @Nullable
-    public Uri getNewIcon() {
-        return mNewIcon;
-    }
-
-    /**
-     * Persists the event to the data store.
-     *
-     * @hide - not meant for public use.
-     */
-    @Override
-    void persist(RcsControllerCall rcsControllerCall) throws RcsMessageStoreException {
-        // TODO ensure failure throws
-        rcsControllerCall.call((iRcs, callingPackage) -> iRcs.createGroupThreadIconChangedEvent(
-                getTimestamp(), getRcsGroupThread().getThreadId(),
-                getOriginatingParticipant().getId(), mNewIcon, callingPackage));
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEventDescriptor.aidl
deleted file mode 100644
index 4bcc5a04..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEventDescriptor.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.telephony.ims;
-
-parcelable RcsGroupThreadIconChangedEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEventDescriptor.java b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEventDescriptor.java
deleted file mode 100644
index 9350e40..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEventDescriptor.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.Uri;
-import android.os.Parcel;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsGroupThreadIconChangedEventDescriptor extends RcsGroupThreadEventDescriptor {
-    private final Uri mNewIcon;
-
-    public RcsGroupThreadIconChangedEventDescriptor(long timestamp, int rcsGroupThreadId,
-            int originatingParticipantId, @Nullable Uri newIcon) {
-        super(timestamp, rcsGroupThreadId, originatingParticipantId);
-        mNewIcon = newIcon;
-    }
-
-    @Override
-    @VisibleForTesting(visibility = PROTECTED)
-    public RcsGroupThreadIconChangedEvent createRcsEvent(RcsControllerCall rcsControllerCall) {
-        return new RcsGroupThreadIconChangedEvent(mTimestamp,
-                new RcsGroupThread(rcsControllerCall, mRcsGroupThreadId),
-                new RcsParticipant(rcsControllerCall, mOriginatingParticipantId), mNewIcon);
-    }
-
-    public static final @NonNull Creator<RcsGroupThreadIconChangedEventDescriptor> CREATOR =
-            new Creator<RcsGroupThreadIconChangedEventDescriptor>() {
-                @Override
-                public RcsGroupThreadIconChangedEventDescriptor createFromParcel(Parcel in) {
-                    return new RcsGroupThreadIconChangedEventDescriptor(in);
-                }
-
-                @Override
-                public RcsGroupThreadIconChangedEventDescriptor[] newArray(int size) {
-                    return new RcsGroupThreadIconChangedEventDescriptor[size];
-                }
-            };
-
-    protected RcsGroupThreadIconChangedEventDescriptor(Parcel in) {
-        super(in);
-        mNewIcon = in.readParcelable(Uri.class.getClassLoader());
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeParcelable(mNewIcon, flags);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
deleted file mode 100644
index a6a0867..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
+++ /dev/null
@@ -1,68 +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.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-/**
- * An event that indicates an {@link RcsGroupThread}'s name was changed. Please see R6-2-5 - GSMA
- * RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @hide
- */
-public final class RcsGroupThreadNameChangedEvent extends RcsGroupThreadEvent {
-    private final String mNewName;
-
-    /**
-     * Creates a new {@link RcsGroupThreadNameChangedEvent}. This event is not persisted into
-     * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
-     *
-     * @param timestamp The timestamp of when this event happened, in milliseconds passed after
-     *                  midnight, January 1st, 1970 UTC
-     * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
-     * @param originatingParticipant The {@link RcsParticipant} that changed the
-     *                               {@link RcsGroupThread}'s icon.
-     * @param newName The new name of the {@link RcsGroupThread}
-     * @see RcsMessageStore#persistRcsEvent(RcsEvent)
-     */
-    public RcsGroupThreadNameChangedEvent(long timestamp, @NonNull RcsGroupThread rcsGroupThread,
-            @NonNull RcsParticipant originatingParticipant, @Nullable String newName) {
-        super(timestamp, rcsGroupThread, originatingParticipant);
-        mNewName = newName;
-    }
-
-    /**
-     * @return Returns the name of this {@link RcsGroupThread} after this
-     * {@link RcsGroupThreadNameChangedEvent} happened.
-     */
-    @Nullable
-    public String getNewName() {
-        return mNewName;
-    }
-
-    /**
-     * Persists the event to the data store.
-     *
-     * @hide - not meant for public use.
-     */
-    @Override
-    void persist(RcsControllerCall rcsControllerCall) throws RcsMessageStoreException {
-        rcsControllerCall.call((iRcs, callingPackage) -> iRcs.createGroupThreadNameChangedEvent(
-                getTimestamp(), getRcsGroupThread().getThreadId(),
-                getOriginatingParticipant().getId(), mNewName, callingPackage));
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEventDescriptor.aidl
deleted file mode 100644
index 480e86b..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEventDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsGroupThreadNameChangedEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEventDescriptor.java b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEventDescriptor.java
deleted file mode 100644
index f9ccdd5..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEventDescriptor.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsGroupThreadNameChangedEventDescriptor extends RcsGroupThreadEventDescriptor {
-    private final String mNewName;
-
-    public RcsGroupThreadNameChangedEventDescriptor(long timestamp, int rcsGroupThreadId,
-            int originatingParticipantId, @Nullable String newName) {
-        super(timestamp, rcsGroupThreadId, originatingParticipantId);
-        mNewName = newName;
-    }
-
-    @Override
-    @VisibleForTesting(visibility = PROTECTED)
-    public RcsGroupThreadNameChangedEvent createRcsEvent(RcsControllerCall rcsControllerCall) {
-        return new RcsGroupThreadNameChangedEvent(
-                mTimestamp,
-                new RcsGroupThread(rcsControllerCall, mRcsGroupThreadId),
-                new RcsParticipant(rcsControllerCall, mOriginatingParticipantId),
-                mNewName);
-    }
-
-    public static final @NonNull Creator<RcsGroupThreadNameChangedEventDescriptor> CREATOR =
-            new Creator<RcsGroupThreadNameChangedEventDescriptor>() {
-                @Override
-                public RcsGroupThreadNameChangedEventDescriptor createFromParcel(Parcel in) {
-                    return new RcsGroupThreadNameChangedEventDescriptor(in);
-                }
-
-                @Override
-                public RcsGroupThreadNameChangedEventDescriptor[] newArray(int size) {
-                    return new RcsGroupThreadNameChangedEventDescriptor[size];
-                }
-            };
-
-    protected RcsGroupThreadNameChangedEventDescriptor(Parcel in) {
-        super(in);
-        mNewName = in.readString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeString(mNewName);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
deleted file mode 100644
index 694c7de..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
+++ /dev/null
@@ -1,70 +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.telephony.ims;
-
-import android.annotation.NonNull;
-
-/**
- * An event that indicates an RCS participant has joined an {@link RcsThread}. Please see US6-3 -
- * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @hide
- */
-public final class RcsGroupThreadParticipantJoinedEvent extends RcsGroupThreadEvent {
-    private final RcsParticipant mJoinedParticipantId;
-
-    /**
-     * Creates a new {@link RcsGroupThreadParticipantJoinedEvent}. This event is not persisted into
-     * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
-     *
-     * @param timestamp              The timestamp of when this event happened, in milliseconds
-     *                               passed after
-     *                               midnight, January 1st, 1970 UTC
-     * @param rcsGroupThread         The {@link RcsGroupThread} that this event happened on
-     * @param originatingParticipant The {@link RcsParticipant} that added or invited the new
-     *                               {@link RcsParticipant} into the {@link RcsGroupThread}
-     * @param joinedParticipant      The new {@link RcsParticipant} that joined the
-     *                               {@link RcsGroupThread}
-     * @see RcsMessageStore#persistRcsEvent(RcsEvent)
-     */
-    public RcsGroupThreadParticipantJoinedEvent(long timestamp,
-            @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant,
-            @NonNull RcsParticipant joinedParticipant) {
-        super(timestamp, rcsGroupThread, originatingParticipant);
-        mJoinedParticipantId = joinedParticipant;
-    }
-
-    /**
-     * @return Returns the {@link RcsParticipant} that joined the associated {@link RcsGroupThread}
-     */
-    public RcsParticipant getJoinedParticipant() {
-        return mJoinedParticipantId;
-    }
-
-    /**
-     * Persists the event to the data store.
-     *
-     * @hide - not meant for public use.
-     */
-    @Override
-    void persist(RcsControllerCall rcsControllerCall) throws RcsMessageStoreException {
-        rcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.createGroupThreadParticipantJoinedEvent(
-                        getTimestamp(),
-                        getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(),
-                        getJoinedParticipant().getId(), callingPackage));
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEventDescriptor.aidl
deleted file mode 100644
index 7210b9f..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEventDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsGroupThreadParticipantJoinedEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEventDescriptor.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEventDescriptor.java
deleted file mode 100644
index 4a6803e..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEventDescriptor.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsGroupThreadParticipantJoinedEventDescriptor extends RcsGroupThreadEventDescriptor {
-    private final int mJoinedParticipantId;
-
-    public RcsGroupThreadParticipantJoinedEventDescriptor(long timestamp, int rcsGroupThreadId,
-            int originatingParticipantId, int joinedParticipantId) {
-        super(timestamp, rcsGroupThreadId, originatingParticipantId);
-        mJoinedParticipantId = joinedParticipantId;
-    }
-
-    @Override
-    @VisibleForTesting(visibility = PROTECTED)
-    public RcsGroupThreadParticipantJoinedEvent createRcsEvent(
-            RcsControllerCall rcsControllerCall) {
-        return new RcsGroupThreadParticipantJoinedEvent(
-                mTimestamp,
-                new RcsGroupThread(rcsControllerCall, mRcsGroupThreadId),
-                new RcsParticipant(rcsControllerCall, mOriginatingParticipantId),
-                new RcsParticipant(rcsControllerCall, mJoinedParticipantId));
-    }
-
-    public static final @NonNull Creator<RcsGroupThreadParticipantJoinedEventDescriptor> CREATOR =
-            new Creator<RcsGroupThreadParticipantJoinedEventDescriptor>() {
-                @Override
-                public RcsGroupThreadParticipantJoinedEventDescriptor createFromParcel(Parcel in) {
-                    return new RcsGroupThreadParticipantJoinedEventDescriptor(in);
-                }
-
-                @Override
-                public RcsGroupThreadParticipantJoinedEventDescriptor[] newArray(int size) {
-                    return new RcsGroupThreadParticipantJoinedEventDescriptor[size];
-                }
-            };
-
-    protected RcsGroupThreadParticipantJoinedEventDescriptor(Parcel in) {
-        super(in);
-        mJoinedParticipantId = in.readInt();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeInt(mJoinedParticipantId);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
deleted file mode 100644
index fec4354..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
+++ /dev/null
@@ -1,67 +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.telephony.ims;
-
-import android.annotation.NonNull;
-
-/**
- * An event that indicates an RCS participant has left an {@link RcsThread}. Please see US6-23 -
- * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @hide
- */
-public final class RcsGroupThreadParticipantLeftEvent extends RcsGroupThreadEvent {
-    private RcsParticipant mLeavingParticipant;
-
-    /**
-     * Creates a new {@link RcsGroupThreadParticipantLeftEvent}. his event is not persisted into
-     * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
-     *
-     * @param timestamp The timestamp of when this event happened, in milliseconds passed after
-     *                  midnight, January 1st, 1970 UTC
-     * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
-     * @param originatingParticipant The {@link RcsParticipant} that removed the
-     *                               {@link RcsParticipant} from the {@link RcsGroupThread}. It is
-     *                               possible that originatingParticipant and leavingParticipant are
-     *                               the same (i.e. {@link RcsParticipant} left the group
-     *                               themselves)
-     * @param leavingParticipant The {@link RcsParticipant} that left the {@link RcsGroupThread}
-     * @see RcsMessageStore#persistRcsEvent(RcsEvent)
-     */
-    public RcsGroupThreadParticipantLeftEvent(long timestamp,
-            @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant,
-            @NonNull RcsParticipant leavingParticipant) {
-        super(timestamp, rcsGroupThread, originatingParticipant);
-        mLeavingParticipant = leavingParticipant;
-    }
-
-    /**
-     * @return Returns the {@link RcsParticipant} that left the associated {@link RcsGroupThread}
-     * after this {@link RcsGroupThreadParticipantLeftEvent} happened.
-     */
-    @NonNull
-    public RcsParticipant getLeavingParticipant() {
-        return mLeavingParticipant;
-    }
-
-    @Override
-    void persist(RcsControllerCall rcsControllerCall) throws RcsMessageStoreException {
-        rcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.createGroupThreadParticipantLeftEvent(getTimestamp(),
-                        getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(),
-                        getLeavingParticipant().getId(), callingPackage));
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEventDescriptor.aidl
deleted file mode 100644
index 3ef92100..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEventDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsGroupThreadParticipantLeftEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEventDescriptor.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEventDescriptor.java
deleted file mode 100644
index 9b1085c..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEventDescriptor.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsGroupThreadParticipantLeftEventDescriptor extends RcsGroupThreadEventDescriptor {
-    private int mLeavingParticipantId;
-
-    public RcsGroupThreadParticipantLeftEventDescriptor(long timestamp, int rcsGroupThreadId,
-            int originatingParticipantId, int leavingParticipantId) {
-        super(timestamp, rcsGroupThreadId, originatingParticipantId);
-        mLeavingParticipantId = leavingParticipantId;
-    }
-
-    @Override
-    @VisibleForTesting(visibility = PROTECTED)
-    public RcsGroupThreadParticipantLeftEvent createRcsEvent(RcsControllerCall rcsControllerCall) {
-        return new RcsGroupThreadParticipantLeftEvent(
-                mTimestamp,
-                new RcsGroupThread(rcsControllerCall, mRcsGroupThreadId),
-                new RcsParticipant(rcsControllerCall, mOriginatingParticipantId),
-                new RcsParticipant(rcsControllerCall, mLeavingParticipantId));
-    }
-
-    @NonNull
-    public static final Parcelable.Creator<RcsGroupThreadParticipantLeftEventDescriptor> CREATOR =
-            new Creator<RcsGroupThreadParticipantLeftEventDescriptor>() {
-                @Override
-                public RcsGroupThreadParticipantLeftEventDescriptor createFromParcel(Parcel in) {
-                    return new RcsGroupThreadParticipantLeftEventDescriptor(in);
-                }
-
-                @Override
-                public RcsGroupThreadParticipantLeftEventDescriptor[] newArray(int size) {
-                    return new RcsGroupThreadParticipantLeftEventDescriptor[size];
-                }
-            };
-
-    protected RcsGroupThreadParticipantLeftEventDescriptor(Parcel in) {
-        super(in);
-        mLeavingParticipantId = in.readInt();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeInt(mLeavingParticipantId);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.java b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
deleted file mode 100644
index 2810a49..0000000
--- a/telephony/java/android/telephony/ims/RcsIncomingMessage.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.WorkerThread;
-
-/**
- * This is a single instance of a message received over RCS.
- *
- * @hide
- */
-public class RcsIncomingMessage extends RcsMessage {
-    /**
-     * @hide
-     */
-    RcsIncomingMessage(RcsControllerCall rcsControllerCall, int id) {
-        super(rcsControllerCall, id);
-    }
-
-    /**
-     * Sets the timestamp of arrival for this message and persists into storage. The timestamp is
-     * defined as milliseconds passed after midnight, January 1, 1970 UTC
-     *
-     * @param arrivalTimestamp The timestamp to set to.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setArrivalTimestamp(long arrivalTimestamp) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setMessageArrivalTimestamp(mId, true,
-                        arrivalTimestamp, callingPackage));
-    }
-
-    /**
-     * @return Returns the timestamp of arrival for this message. The timestamp is defined as
-     * milliseconds passed after midnight, January 1, 1970 UTC
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public long getArrivalTimestamp() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getMessageArrivalTimestamp(mId, true,
-                        callingPackage));
-    }
-
-    /**
-     * Sets the timestamp of when the user saw this message and persists into storage. The timestamp
-     * is defined as milliseconds passed after midnight, January 1, 1970 UTC
-     *
-     * @param notifiedTimestamp The timestamp to set to.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setSeenTimestamp(long notifiedTimestamp) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setMessageSeenTimestamp(mId, true, notifiedTimestamp,
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns the timestamp of when the user saw this message. The timestamp is defined as
-     * milliseconds passed after midnight, January 1, 1970 UTC
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public long getSeenTimestamp() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getMessageSeenTimestamp(mId, true, callingPackage));
-    }
-
-    /**
-     * @return Returns the sender of this incoming message.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public RcsParticipant getSenderParticipant() throws RcsMessageStoreException {
-        return new RcsParticipant(
-                mRcsControllerCall,
-                mRcsControllerCall.call(
-                        (iRcs, callingPackage) -> iRcs.getSenderParticipant(mId, callingPackage)));
-    }
-
-    /**
-     * @return Returns {@code true} as this is an incoming message
-     */
-    @Override
-    public boolean isIncoming() {
-        return true;
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl
deleted file mode 100644
index 1f1d4f6..0000000
--- a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsIncomingMessageCreationParams;
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
deleted file mode 100644
index d95dc4f..0000000
--- a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.CheckResult;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * {@link RcsIncomingMessageCreationParams} is a collection of parameters that should be passed
- * into {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} to generate an
- * {@link RcsIncomingMessage} on that {@link RcsThread}
- *
- * @hide
- */
-public final class RcsIncomingMessageCreationParams extends RcsMessageCreationParams implements
-        Parcelable {
-    // The arrival timestamp for the RcsIncomingMessage to be created
-    private final long mArrivalTimestamp;
-    // The seen timestamp for the RcsIncomingMessage to be created
-    private final long mSeenTimestamp;
-    // The participant that sent this incoming message
-    private final int mSenderParticipantId;
-
-    /**
-     * Builder to help create an {@link RcsIncomingMessageCreationParams}
-     *
-     * @see RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)
-     */
-    public static class Builder extends RcsMessageCreationParams.Builder {
-        private RcsParticipant mSenderParticipant;
-        private long mArrivalTimestamp;
-        private long mSeenTimestamp;
-
-        /**
-         * Creates a {@link Builder} to create an instance of
-         * {@link RcsIncomingMessageCreationParams}
-         *
-         * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination
-         *                             timestamp value in milliseconds passed after midnight,
-         *                             January 1, 1970 UTC
-         * @param arrivalTimestamp The timestamp of arrival, defined as milliseconds passed after
-         *                         midnight, January 1, 1970 UTC
-         * @param subscriptionId The subscription ID that was used to send or receive this
-         *                       {@link RcsMessage}
-         */
-        public Builder(long originationTimestamp, long arrivalTimestamp, int subscriptionId) {
-            super(originationTimestamp, subscriptionId);
-            mArrivalTimestamp = arrivalTimestamp;
-        }
-
-        /**
-         * Sets the {@link RcsParticipant} that send this {@link RcsIncomingMessage}
-         *
-         * @param senderParticipant The {@link RcsParticipant} that sent this
-         * {@link RcsIncomingMessage}
-         * @return The same instance of {@link Builder} to chain methods.
-         */
-        @CheckResult
-        public Builder setSenderParticipant(RcsParticipant senderParticipant) {
-            mSenderParticipant = senderParticipant;
-            return this;
-        }
-
-        /**
-         * Sets the time of the arrival of this {@link RcsIncomingMessage}
-
-         * @return The same instance of {@link Builder} to chain methods.
-         * @see RcsIncomingMessage#setArrivalTimestamp(long)
-         */
-        @CheckResult
-        public Builder setArrivalTimestamp(long arrivalTimestamp) {
-            mArrivalTimestamp = arrivalTimestamp;
-            return this;
-        }
-
-        /**
-         * Sets the time of the when this user saw the {@link RcsIncomingMessage}
-         * @param seenTimestamp The seen timestamp , defined as milliseconds passed after midnight,
-         *                      January 1, 1970 UTC
-         * @return The same instance of {@link Builder} to chain methods.
-         * @see RcsIncomingMessage#setSeenTimestamp(long)
-         */
-        @CheckResult
-        public Builder setSeenTimestamp(long seenTimestamp) {
-            mSeenTimestamp = seenTimestamp;
-            return this;
-        }
-
-        /**
-         * Creates parameters for creating a new incoming message.
-         * @return A new instance of {@link RcsIncomingMessageCreationParams} to create a new
-         * {@link RcsIncomingMessage}
-         */
-        public RcsIncomingMessageCreationParams build() {
-            return new RcsIncomingMessageCreationParams(this);
-        }
-    }
-
-    private RcsIncomingMessageCreationParams(Builder builder) {
-        super(builder);
-        mArrivalTimestamp = builder.mArrivalTimestamp;
-        mSeenTimestamp = builder.mSeenTimestamp;
-        mSenderParticipantId = builder.mSenderParticipant.getId();
-    }
-
-    private RcsIncomingMessageCreationParams(Parcel in) {
-        super(in);
-        mArrivalTimestamp = in.readLong();
-        mSeenTimestamp = in.readLong();
-        mSenderParticipantId = in.readInt();
-    }
-
-    /**
-     * @return Returns the arrival timestamp for the {@link RcsIncomingMessage} to be created.
-     * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC
-     */
-    public long getArrivalTimestamp() {
-        return mArrivalTimestamp;
-    }
-
-    /**
-     * @return Returns the seen timestamp for the {@link RcsIncomingMessage} to be created.
-     * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC
-     */
-    public long getSeenTimestamp() {
-        return mSeenTimestamp;
-    }
-
-    /**
-     * Helper getter for {@link com.android.internal.telephony.ims.RcsMessageStoreController} to
-     * create {@link RcsIncomingMessage}s
-     *
-     * Since the API doesn't expose any ID's to API users, this should be hidden.
-     * @hide
-     */
-    public int getSenderParticipantId() {
-        return mSenderParticipantId;
-    }
-
-    public static final @NonNull Creator<RcsIncomingMessageCreationParams> CREATOR =
-            new Creator<RcsIncomingMessageCreationParams>() {
-                @Override
-                public RcsIncomingMessageCreationParams createFromParcel(Parcel in) {
-                    return new RcsIncomingMessageCreationParams(in);
-                }
-
-                @Override
-                public RcsIncomingMessageCreationParams[] newArray(int size) {
-                    return new RcsIncomingMessageCreationParams[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest);
-        dest.writeLong(mArrivalTimestamp);
-        dest.writeLong(mSeenTimestamp);
-        dest.writeInt(mSenderParticipantId);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessage.java b/telephony/java/android/telephony/ims/RcsMessage.java
deleted file mode 100644
index 4601bfd..0000000
--- a/telephony/java/android/telephony/ims/RcsMessage.java
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.WorkerThread;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * This is a single instance of a message sent or received over RCS.
- *
- * @hide
- */
-public abstract class RcsMessage {
-    /**
-     * The value to indicate that this {@link RcsMessage} does not have any location information.
-     */
-    public static final double LOCATION_NOT_SET = Double.MIN_VALUE;
-
-    /**
-     * The status to indicate that this {@link RcsMessage}s status is not set yet.
-     */
-    public static final int NOT_SET = 0;
-
-    /**
-     * The status to indicate that this {@link RcsMessage} is a draft and is not in the process of
-     * sending yet.
-     */
-    public static final int DRAFT = 1;
-
-    /**
-     * The status to indicate that this {@link RcsMessage} was successfully sent.
-     */
-    public static final int QUEUED = 2;
-
-    /**
-     * The status to indicate that this {@link RcsMessage} is actively being sent.
-     */
-    public static final int SENDING = 3;
-
-    /**
-     * The status to indicate that this {@link RcsMessage} was successfully sent.
-     */
-    public static final int SENT = 4;
-
-    /**
-     * The status to indicate that this {@link RcsMessage} failed to send in an attempt before, and
-     * now being retried.
-     */
-    public static final int RETRYING = 5;
-
-    /**
-     * The status to indicate that this {@link RcsMessage} has permanently failed to send.
-     */
-    public static final int FAILED = 6;
-
-    /**
-     * The status to indicate that this {@link RcsMessage} was successfully received.
-     */
-    public static final int RECEIVED = 7;
-
-    /**
-     * The status to indicate that this {@link RcsMessage} was seen.
-     */
-    public static final int SEEN = 9;
-
-    /**
-     * @hide
-     */
-    protected final RcsControllerCall mRcsControllerCall;
-
-    /**
-     * @hide
-     */
-    protected final int mId;
-
-    @IntDef({
-            DRAFT, QUEUED, SENDING, SENT, RETRYING, FAILED, RECEIVED, SEEN
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RcsMessageStatus {
-    }
-
-    RcsMessage(RcsControllerCall rcsControllerCall, int id) {
-        mRcsControllerCall = rcsControllerCall;
-        mId = id;
-    }
-
-    /**
-     * Returns the row Id from the common message.
-     *
-     * @hide
-     */
-    public int getId() {
-        return mId;
-    }
-
-    /**
-     * @return Returns the subscription ID that this {@link RcsMessage} was sent from, or delivered
-     * to.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     * @see android.telephony.SubscriptionInfo#getSubscriptionId
-     */
-    public int getSubscriptionId() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getMessageSubId(mId, isIncoming(), callingPackage));
-    }
-
-    /**
-     * Sets the subscription ID that this {@link RcsMessage} was sent from, or delivered to and
-     * persists it into storage.
-     *
-     * @param subId The subscription ID to persists into storage.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     * @see android.telephony.SubscriptionInfo#getSubscriptionId
-     */
-    @WorkerThread
-    public void setSubscriptionId(int subId) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setMessageSubId(mId, isIncoming(), subId,
-                        callingPackage));
-    }
-
-    /**
-     * Sets the status of this message and persists it into storage. Please see
-     * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers.
-     *
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setStatus(@RcsMessageStatus int rcsMessageStatus) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setMessageStatus(mId, isIncoming(), rcsMessageStatus,
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns the status of this message. Please see
-     * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public @RcsMessageStatus int getStatus() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getMessageStatus(mId, isIncoming(), callingPackage));
-    }
-
-    /**
-     * Sets the origination timestamp of this message and persists it into storage. Origination is
-     * defined as when the sender tapped the send button.
-     *
-     * @param timestamp The origination timestamp value in milliseconds passed after midnight,
-     *                  January 1, 1970 UTC
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setOriginationTimestamp(long timestamp) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setMessageOriginationTimestamp(mId, isIncoming(),
-                        timestamp, callingPackage));
-    }
-
-    /**
-     * @return Returns the origination timestamp of this message in milliseconds passed after
-     * midnight, January 1, 1970 UTC. Origination is defined as when the sender tapped the send
-     * button.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public long getOriginationTimestamp() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getMessageOriginationTimestamp(mId, isIncoming(),
-                        callingPackage));
-    }
-
-    /**
-     * Sets the globally unique RCS message identifier for this message and persists it into
-     * storage. This function does not confirm that this message id is unique. Please see 4.4.5.2
-     * - GSMA RCC.53 (RCS Device API 1.6 Specification
-     *
-     * @param rcsMessageGlobalId The globally RCS message identifier
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setRcsMessageId(String rcsMessageGlobalId) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setGlobalMessageIdForMessage(mId, isIncoming(),
-                        rcsMessageGlobalId, callingPackage));
-    }
-
-    /**
-     * @return Returns the globally unique RCS message identifier for this message. Please see
-     * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public String getRcsMessageId() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getGlobalMessageIdForMessage(mId, isIncoming(),
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns the user visible text included in this message.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public String getText() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getTextForMessage(mId, isIncoming(),
-                        callingPackage));
-    }
-
-    /**
-     * Sets the user visible text for this message and persists in storage.
-     *
-     * @param text The text this message now has
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setText(String text) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setTextForMessage(mId, isIncoming(), text,
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns the associated latitude for this message, or
-     * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public double getLatitude() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getLatitudeForMessage(mId, isIncoming(),
-                        callingPackage));
-    }
-
-    /**
-     * Sets the latitude for this message and persists in storage.
-     *
-     * @param latitude The latitude for this location message.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setLatitude(double latitude) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setLatitudeForMessage(mId, isIncoming(), latitude,
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns the associated longitude for this message, or
-     * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public double getLongitude() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getLongitudeForMessage(mId, isIncoming(),
-                        callingPackage));
-    }
-
-    /**
-     * Sets the longitude for this message and persists in storage.
-     *
-     * @param longitude The longitude for this location message.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setLongitude(double longitude) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setLongitudeForMessage(mId, isIncoming(), longitude,
-                        callingPackage));
-    }
-
-    /**
-     * Attaches an {@link RcsFileTransferPart} to this message and persists into storage.
-     *
-     * @param fileTransferCreationParameters The parameters to be used to create the
-     *                                       {@link RcsFileTransferPart}
-     * @return A new instance of {@link RcsFileTransferPart}
-     * @throws RcsMessageStoreException if the file transfer could not be persisted into storage.
-     */
-    @NonNull
-    @WorkerThread
-    public RcsFileTransferPart insertFileTransfer(
-            RcsFileTransferCreationParams fileTransferCreationParameters)
-            throws RcsMessageStoreException {
-        return new RcsFileTransferPart(mRcsControllerCall, mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.storeFileTransfer(mId, isIncoming(),
-                        fileTransferCreationParameters, callingPackage)));
-    }
-
-    /**
-     * @return Returns all the {@link RcsFileTransferPart}s associated with this message in an
-     * unmodifiable set.
-     * @throws RcsMessageStoreException if the file transfers could not be read from the storage
-     */
-    @NonNull
-    @WorkerThread
-    public Set<RcsFileTransferPart> getFileTransferParts() throws RcsMessageStoreException {
-        Set<RcsFileTransferPart> fileTransferParts = new HashSet<>();
-
-        int[] fileTransferIds = mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getFileTransfersAttachedToMessage(mId, isIncoming(),
-                        callingPackage));
-
-        for (int fileTransfer : fileTransferIds) {
-            fileTransferParts.add(new RcsFileTransferPart(mRcsControllerCall, fileTransfer));
-        }
-
-        return Collections.unmodifiableSet(fileTransferParts);
-    }
-
-    /**
-     * Removes a {@link RcsFileTransferPart} from this message, and deletes it in storage.
-     *
-     * @param fileTransferPart The part to delete.
-     * @throws RcsMessageStoreException if the file transfer could not be removed from storage
-     */
-    @WorkerThread
-    public void removeFileTransferPart(@NonNull RcsFileTransferPart fileTransferPart)
-            throws RcsMessageStoreException {
-        if (fileTransferPart == null) {
-            return;
-        }
-
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.deleteFileTransfer(fileTransferPart.getId(),
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns {@code true} if this message was received on this device, {@code false} if it
-     * was sent.
-     */
-    public abstract boolean isIncoming();
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
deleted file mode 100644
index f0eea88..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import static android.telephony.ims.RcsMessage.LOCATION_NOT_SET;
-
-import android.annotation.CheckResult;
-import android.annotation.Nullable;
-import android.os.Parcel;
-
-/**
- * The collection of parameters to be passed into
- * {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} and
- * {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to create and persist
- * {@link RcsMessage}s on an {@link RcsThread}
- *
- * @hide
- */
-public class RcsMessageCreationParams {
-    // The globally unique id of the RcsMessage to be created.
-    private final String mRcsMessageGlobalId;
-
-    // The subscription that this message was/will be received/sent from.
-    private final int mSubId;
-    // The sending/receiving status of the message
-    private final @RcsMessage.RcsMessageStatus int mMessageStatus;
-    // The timestamp of message creation
-    private final long mOriginationTimestamp;
-    // The user visible content of the message
-    private final String mText;
-    // The latitude of the message if this is a location message
-    private final double mLatitude;
-    // The longitude of the message if this is a location message
-    private final double mLongitude;
-
-    /**
-     * @return Returns the globally unique RCS Message ID for the {@link RcsMessage} to be created.
-     * Please see 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
-     */
-    @Nullable
-    public String getRcsMessageGlobalId() {
-        return mRcsMessageGlobalId;
-    }
-
-    /**
-     * @return Returns the subscription ID that was used to send or receive the {@link RcsMessage}
-     * to be created.
-     */
-    public int getSubId() {
-        return mSubId;
-    }
-
-    /**
-     * @return Returns the status for the {@link RcsMessage} to be created.
-     * @see RcsMessage.RcsMessageStatus
-     */
-    public int getMessageStatus() {
-        return mMessageStatus;
-    }
-
-    /**
-     * @return Returns the origination timestamp of the {@link RcsMessage} to be created in
-     * milliseconds passed after midnight, January 1, 1970 UTC. Origination is defined as when
-     * the sender tapped the send button.
-     */
-    public long getOriginationTimestamp() {
-        return mOriginationTimestamp;
-    }
-
-    /**
-     * @return Returns the user visible text contained in the {@link RcsMessage} to be created
-     */
-    @Nullable
-    public String getText() {
-        return mText;
-    }
-
-    /**
-     * @return Returns the latitude of the {@link RcsMessage} to be created, or
-     * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location.
-     */
-    public double getLatitude() {
-        return mLatitude;
-    }
-
-    /**
-     * @return Returns the longitude of the {@link RcsMessage} to be created, or
-     * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location.
-     */
-    public double getLongitude() {
-        return mLongitude;
-    }
-
-    /**
-     * The base builder for creating {@link RcsMessage}s on {@link RcsThread}s.
-     *
-     * @see RcsIncomingMessageCreationParams
-     */
-    public static class Builder {
-        private String mRcsMessageGlobalId;
-        private int mSubId;
-        private @RcsMessage.RcsMessageStatus int mMessageStatus;
-        private long mOriginationTimestamp;
-        private String mText;
-        private double mLatitude = LOCATION_NOT_SET;
-        private double mLongitude = LOCATION_NOT_SET;
-
-        /**
-         * @hide
-         */
-        public Builder(long originationTimestamp, int subscriptionId) {
-            mOriginationTimestamp = originationTimestamp;
-            mSubId = subscriptionId;
-        }
-
-        /**
-         * Sets the status of the {@link RcsMessage} to be built.
-         *
-         * @param rcsMessageStatus The status to be set
-         * @return The same instance of {@link Builder} to chain methods
-         * @see RcsMessage#setStatus(int)
-         */
-        @CheckResult
-        public Builder setStatus(@RcsMessage.RcsMessageStatus int rcsMessageStatus) {
-            mMessageStatus = rcsMessageStatus;
-            return this;
-        }
-
-        /**
-         * Sets the globally unique RCS message identifier for the {@link RcsMessage} to be built.
-         * This function does not confirm that this message id is unique. Please see 4.4.5.2 - GSMA
-         * RCC.53 (RCS Device API 1.6 Specification)
-         *
-         * @param rcsMessageId The ID to be set
-         * @return The same instance of {@link Builder} to chain methods
-         * @see RcsMessage#setRcsMessageId(String)
-         */
-        @CheckResult
-        public Builder setRcsMessageId(String rcsMessageId) {
-            mRcsMessageGlobalId = rcsMessageId;
-            return this;
-        }
-
-        /**
-         * Sets the text of the {@link RcsMessage} to be built.
-         *
-         * @param text The user visible text of the message
-         * @return The same instance of {@link Builder} to chain methods
-         * @see RcsMessage#setText(String)
-         */
-        @CheckResult
-        public Builder setText(String text) {
-            mText = text;
-            return this;
-        }
-
-        /**
-         * Sets the latitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71
-         * (RCS Universal Profile Service Definition Document)
-         *
-         * @param latitude The latitude of the location information associated with this message.
-         * @return The same instance of {@link Builder} to chain methods
-         * @see RcsMessage#setLatitude(double)
-         */
-        @CheckResult
-        public Builder setLatitude(double latitude) {
-            mLatitude = latitude;
-            return this;
-        }
-
-        /**
-         * Sets the longitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71
-         * (RCS Universal Profile Service Definition Document)
-         *
-         * @param longitude The longitude of the location information associated with this message.
-         * @return The same instance of {@link Builder} to chain methods
-         * @see RcsMessage#setLongitude(double)
-         */
-        @CheckResult
-        public Builder setLongitude(double longitude) {
-            mLongitude = longitude;
-            return this;
-        }
-
-        /**
-         * @return Builds and returns a newly created {@link RcsMessageCreationParams}
-         */
-        public RcsMessageCreationParams build() {
-            return new RcsMessageCreationParams(this);
-        }
-    }
-
-    protected RcsMessageCreationParams(Builder builder) {
-        mRcsMessageGlobalId = builder.mRcsMessageGlobalId;
-        mSubId = builder.mSubId;
-        mMessageStatus = builder.mMessageStatus;
-        mOriginationTimestamp = builder.mOriginationTimestamp;
-        mText = builder.mText;
-        mLatitude = builder.mLatitude;
-        mLongitude = builder.mLongitude;
-    }
-
-    /**
-     * @hide
-     */
-    RcsMessageCreationParams(Parcel in) {
-        mRcsMessageGlobalId = in.readString();
-        mSubId = in.readInt();
-        mMessageStatus = in.readInt();
-        mOriginationTimestamp = in.readLong();
-        mText = in.readString();
-        mLatitude = in.readDouble();
-        mLongitude = in.readDouble();
-    }
-
-    /**
-     * @hide
-     */
-    public void writeToParcel(Parcel dest) {
-        dest.writeString(mRcsMessageGlobalId);
-        dest.writeInt(mSubId);
-        dest.writeInt(mMessageStatus);
-        dest.writeLong(mOriginationTimestamp);
-        dest.writeString(mText);
-        dest.writeDouble(mLatitude);
-        dest.writeDouble(mLongitude);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageManager.java b/telephony/java/android/telephony/ims/RcsMessageManager.java
deleted file mode 100644
index a1c7c0f..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageManager.java
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemService;
-import android.annotation.WorkerThread;
-import android.content.Context;
-import android.net.Uri;
-
-import java.util.List;
-
-/**
- * RcsMessageManager is the application interface to RcsProvider and provides access methods to
- * RCS related database tables.
- *
- * @hide
- */
-@SystemService(Context.TELEPHONY_RCS_MESSAGE_SERVICE)
-public class RcsMessageManager {
-    RcsControllerCall mRcsControllerCall;
-
-    /**
-     * Use {@link Context#getSystemService(String)} to get an instance of this service.
-     * @hide
-     */
-    public RcsMessageManager(Context context) {
-        mRcsControllerCall = new RcsControllerCall(context);
-    }
-
-    /**
-     * Returns the first chunk of existing {@link RcsThread}s in the common storage.
-     *
-     * @param queryParameters Parameters to specify to return a subset of all RcsThreads.
-     *                        Passing a value of null will return all threads.
-     * @throws RcsMessageStoreException if the query could not be completed on the storage
-     */
-    @WorkerThread
-    @NonNull
-    public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParams queryParameters)
-            throws RcsMessageStoreException {
-        return new RcsThreadQueryResult(mRcsControllerCall,
-                mRcsControllerCall.call(
-                        (iRcs, callingPackage) -> iRcs.getRcsThreads(queryParameters,
-                                callingPackage)));
-    }
-
-    /**
-     * Returns the next chunk of {@link RcsThread}s in the common storage.
-     *
-     * @param continuationToken A token to continue the query to get the next chunk. This is
-     *                          obtained through {@link RcsThreadQueryResult#getContinuationToken}.
-     * @throws RcsMessageStoreException if the query could not be completed on the storage
-     */
-    @WorkerThread
-    @NonNull
-    public RcsThreadQueryResult getRcsThreads(@NonNull RcsQueryContinuationToken continuationToken)
-            throws RcsMessageStoreException {
-        return new RcsThreadQueryResult(mRcsControllerCall,
-                mRcsControllerCall.call(
-                        (iRcs, callingPackage) -> iRcs.getRcsThreadsWithToken(continuationToken,
-                                callingPackage)));
-    }
-
-    /**
-     * Returns the first chunk of existing {@link RcsParticipant}s in the common storage.
-     *
-     * @param queryParameters Parameters to specify to return a subset of all RcsParticipants.
-     *                        Passing a value of null will return all participants.
-     * @throws RcsMessageStoreException if the query could not be completed on the storage
-     */
-    @WorkerThread
-    @NonNull
-    public RcsParticipantQueryResult getRcsParticipants(
-            @Nullable RcsParticipantQueryParams queryParameters)
-            throws RcsMessageStoreException {
-        return new RcsParticipantQueryResult(mRcsControllerCall,
-                mRcsControllerCall.call(
-                        (iRcs, callingPackage) -> iRcs.getParticipants(queryParameters,
-                                callingPackage)));
-    }
-
-    /**
-     * Returns the next chunk of {@link RcsParticipant}s in the common storage.
-     *
-     * @param continuationToken A token to continue the query to get the next chunk. This is
-     *                          obtained through
-     *                          {@link RcsParticipantQueryResult#getContinuationToken}
-     * @throws RcsMessageStoreException if the query could not be completed on the storage
-     */
-    @WorkerThread
-    @NonNull
-    public RcsParticipantQueryResult getRcsParticipants(
-            @NonNull RcsQueryContinuationToken continuationToken)
-            throws RcsMessageStoreException {
-        return new RcsParticipantQueryResult(mRcsControllerCall,
-                mRcsControllerCall.call(
-                        (iRcs, callingPackage) -> iRcs.getParticipantsWithToken(continuationToken,
-                                callingPackage)));
-    }
-
-    /**
-     * Returns the first chunk of existing {@link RcsMessage}s in the common storage.
-     *
-     * @param queryParams Parameters to specify to return a subset of all RcsMessages.
-     *                    Passing a value of null will return all messages.
-     * @throws RcsMessageStoreException if the query could not be completed on the storage
-     */
-    @WorkerThread
-    @NonNull
-    public RcsMessageQueryResult getRcsMessages(
-            @Nullable RcsMessageQueryParams queryParams) throws RcsMessageStoreException {
-        return new RcsMessageQueryResult(mRcsControllerCall,
-                mRcsControllerCall.call(
-                        (iRcs, callingPackage) -> iRcs.getMessages(queryParams, callingPackage)));
-    }
-
-    /**
-     * Returns the next chunk of {@link RcsMessage}s in the common storage.
-     *
-     * @param continuationToken A token to continue the query to get the next chunk. This is
-     *                          obtained through {@link RcsMessageQueryResult#getContinuationToken}
-     * @throws RcsMessageStoreException if the query could not be completed on the storage
-     */
-    @WorkerThread
-    @NonNull
-    public RcsMessageQueryResult getRcsMessages(
-            @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException {
-        return new RcsMessageQueryResult(mRcsControllerCall,
-                mRcsControllerCall.call(
-                        (iRcs, callingPackage) -> iRcs.getMessagesWithToken(continuationToken,
-                                callingPackage)));
-    }
-
-    /**
-     * Returns the first chunk of existing {@link RcsEvent}s in the common storage.
-     *
-     * @param queryParams Parameters to specify to return a subset of all RcsEvents.
-     *                    Passing a value of null will return all events.
-     * @throws RcsMessageStoreException if the query could not be completed on the storage
-     */
-    @WorkerThread
-    @NonNull
-    public RcsEventQueryResult getRcsEvents(
-            @Nullable RcsEventQueryParams queryParams) throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getEvents(queryParams, callingPackage))
-                .getRcsEventQueryResult(mRcsControllerCall);
-    }
-
-    /**
-     * Returns the next chunk of {@link RcsEvent}s in the common storage.
-     *
-     * @param continuationToken A token to continue the query to get the next chunk. This is
-     *                          obtained through {@link RcsEventQueryResult#getContinuationToken}.
-     * @throws RcsMessageStoreException if the query could not be completed on the storage
-     */
-    @WorkerThread
-    @NonNull
-    public RcsEventQueryResult getRcsEvents(
-            @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getEventsWithToken(continuationToken,
-                        callingPackage))
-                .getRcsEventQueryResult(mRcsControllerCall);
-    }
-
-    /**
-     * Persists an {@link RcsEvent} to common storage.
-     *
-     * @param rcsEvent The {@link RcsEvent} to persist into storage.
-     * @throws RcsMessageStoreException if the query could not be completed on the storage
-     * @see RcsGroupThreadNameChangedEvent
-     * @see RcsGroupThreadIconChangedEvent
-     * @see RcsGroupThreadParticipantJoinedEvent
-     * @see RcsGroupThreadParticipantLeftEvent
-     * @see RcsParticipantAliasChangedEvent
-     */
-    @WorkerThread
-    @NonNull
-    public void persistRcsEvent(RcsEvent rcsEvent) throws RcsMessageStoreException {
-        rcsEvent.persist(mRcsControllerCall);
-    }
-
-    /**
-     * Creates a new 1 to 1 thread with the given participant and persists it in the storage.
-     *
-     * @param recipient The {@link RcsParticipant} that will receive the messages in this thread.
-     * @return The newly created {@link Rcs1To1Thread}
-     * @throws RcsMessageStoreException if the thread could not be persisted in the storage
-     */
-    @WorkerThread
-    @NonNull
-    public Rcs1To1Thread createRcs1To1Thread(@NonNull RcsParticipant recipient)
-            throws RcsMessageStoreException {
-        return new Rcs1To1Thread(
-                mRcsControllerCall,
-                mRcsControllerCall.call(
-                        (iRcs, callingPackage) -> iRcs.createRcs1To1Thread(recipient.getId(),
-                                callingPackage)));
-    }
-
-    /**
-     * Creates a new group thread with the given participants and persists it in the storage.
-     *
-     * @throws RcsMessageStoreException if the thread could not be persisted in the storage
-     */
-    @WorkerThread
-    @NonNull
-    public RcsGroupThread createGroupThread(@Nullable List<RcsParticipant> recipients,
-            @Nullable String groupName, @Nullable Uri groupIcon) throws RcsMessageStoreException {
-        int[] recipientIds = null;
-        if (recipients != null) {
-            recipientIds = new int[recipients.size()];
-
-            for (int i = 0; i < recipients.size(); i++) {
-                recipientIds[i] = recipients.get(i).getId();
-            }
-        }
-
-        int[] finalRecipientIds = recipientIds;
-
-        int threadId = mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.createGroupThread(finalRecipientIds, groupName,
-                        groupIcon, callingPackage));
-
-        return new RcsGroupThread(mRcsControllerCall, threadId);
-    }
-
-    /**
-     * Delete the given {@link RcsThread} from the storage.
-     *
-     * @param thread The thread to be deleted.
-     * @throws RcsMessageStoreException if the thread could not be deleted from the storage
-     */
-    @WorkerThread
-    public void deleteThread(@NonNull RcsThread thread) throws RcsMessageStoreException {
-        if (thread == null) {
-            return;
-        }
-
-        boolean isDeleteSucceeded = mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.deleteThread(thread.getThreadId(),
-                        thread.getThreadType(), callingPackage));
-
-        if (!isDeleteSucceeded) {
-            throw new RcsMessageStoreException("Could not delete RcsThread");
-        }
-    }
-
-    /**
-     * Creates a new participant and persists it in the storage.
-     *
-     * @param canonicalAddress The defining address (e.g. phone number) of the participant.
-     * @param alias            The RCS alias for the participant.
-     * @throws RcsMessageStoreException if the participant could not be created on the storage
-     */
-    @WorkerThread
-    @NonNull
-    public RcsParticipant createRcsParticipant(String canonicalAddress, @Nullable String alias)
-            throws RcsMessageStoreException {
-        return new RcsParticipant(mRcsControllerCall, mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.createRcsParticipant(canonicalAddress, alias,
-                        callingPackage)));
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
deleted file mode 100644
index 9f9eafb..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.CheckResult;
-import android.annotation.IntDef;
-import android.annotation.IntRange;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.security.InvalidParameterException;
-
-/**
- * The parameters to pass into
- * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} in order to select a
- * subset of {@link RcsMessage}s present in the message store.
- *
- * @hide
- */
-public final class RcsMessageQueryParams implements Parcelable {
-    /**
-     * @hide - not meant for public use
-     */
-    public static final int THREAD_ID_NOT_SET = -1;
-
-    /**
-     * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
-     * be sorted in the same order of {@link RcsMessage}s that got persisted into storage for faster
-     * results.
-     */
-    public static final int SORT_BY_CREATION_ORDER = 0;
-
-    /**
-     * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
-     * be sorted according to the timestamp of {@link RcsMessage#getOriginationTimestamp()}
-     */
-    public static final int SORT_BY_TIMESTAMP = 1;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
-    public @interface SortingProperty {
-    }
-
-    /**
-     * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make
-     * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return
-     * {@link RcsIncomingMessage}s.
-     */
-    public static final int MESSAGE_TYPE_INCOMING = 0x0001;
-
-    /**
-     * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make
-     * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return
-     * {@link RcsOutgoingMessage}s.
-     */
-    public static final int MESSAGE_TYPE_OUTGOING = 0x0002;
-
-    /**
-     * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make
-     * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return {@link RcsMessage}s
-     * that have an {@link RcsFileTransferPart} attached.
-     */
-    public static final int MESSAGES_WITH_FILE_TRANSFERS = 0x0004;
-
-    /**
-     * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make
-     * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return {@link RcsMessage}s
-     * that don't have an {@link RcsFileTransferPart} attached.
-     */
-    public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 0x0008;
-
-    /**
-     * @hide - not meant for public use
-     */
-    public static final String MESSAGE_QUERY_PARAMETERS_KEY = "message_query_parameters";
-
-    // Whether the result should be filtered against incoming or outgoing messages
-    private int mMessageType;
-    // Whether the result should have file transfer messages attached or not
-    private int mFileTransferPresence;
-    // The SQL "Like" clause to filter messages
-    private String mMessageLike;
-    // The property the messages should be sorted against
-    private @SortingProperty int mSortingProperty;
-    // Whether the messages should be sorted in ascending order
-    private boolean mIsAscending;
-    // The number of results that should be returned with this query
-    private int mLimit;
-    // The thread that the results should be limited to
-    private int mThreadId;
-
-    RcsMessageQueryParams(int messageType, int fileTransferPresence, String messageLike,
-            int threadId, @SortingProperty int sortingProperty, boolean isAscending, int limit) {
-        mMessageType = messageType;
-        mFileTransferPresence = fileTransferPresence;
-        mMessageLike = messageLike;
-        mSortingProperty = sortingProperty;
-        mIsAscending = isAscending;
-        mLimit = limit;
-        mThreadId = threadId;
-    }
-
-    /**
-     * @return Returns the type of {@link RcsMessage}s that this {@link RcsMessageQueryParams}
-     * is set to query for.
-     */
-    public int getMessageType() {
-        return mMessageType;
-    }
-
-    /**
-     * @return Returns whether the result query should return {@link RcsMessage}s with
-     * {@link RcsFileTransferPart}s or not
-     */
-    public int getFileTransferPresence() {
-        return mFileTransferPresence;
-    }
-
-    /**
-     * @return Returns the SQL-inspired "LIKE" clause that will be used to match {@link RcsMessage}s
-     */
-    public String getMessageLike() {
-        return mMessageLike;
-    }
-
-    /**
-     * @return Returns the number of {@link RcsThread}s to be returned from the query. A value of
-     * 0 means there is no set limit.
-     */
-    public int getLimit() {
-        return mLimit;
-    }
-
-    /**
-     * @return Returns the property that will be used to sort the result against.
-     * @see SortingProperty
-     */
-    public @SortingProperty int getSortingProperty() {
-        return mSortingProperty;
-    }
-
-    /**
-     * @return Returns {@code true} if the result set will be sorted in ascending order,
-     * {@code false} if it will be sorted in descending order.
-     */
-    public boolean getSortDirection() {
-        return mIsAscending;
-    }
-
-    /**
-     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
-     * the thread that the result query should be limited to.
-     *
-     * As we do not expose any sort of integer ID's to public usage, this should be hidden.
-     *
-     * @hide - not meant for public use
-     */
-    public int getThreadId() {
-        return mThreadId;
-    }
-
-    /**
-     * A helper class to build the {@link RcsMessageQueryParams}.
-     */
-    public static class Builder {
-        private @SortingProperty int mSortingProperty;
-        private int mMessageType;
-        private int mFileTransferPresence;
-        private String mMessageLike;
-        private boolean mIsAscending;
-        private int mLimit = 100;
-        private int mThreadId = THREAD_ID_NOT_SET;
-
-        /**
-         * Creates a new builder for {@link RcsMessageQueryParams} to be used in
-         * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
-         *
-         */
-        public Builder() {
-            // empty implementation
-        }
-
-        /**
-         * Desired number of threads to be returned from the query. Passing in 0 will return all
-         * existing threads at once. The limit defaults to 100.
-         *
-         * @param limit The number to limit the query result to.
-         * @return The same instance of the builder to chain parameters.
-         * @throws InvalidParameterException If the given limit is negative.
-         */
-        @CheckResult
-        public Builder setResultLimit(@IntRange(from = 0) int limit)
-                throws InvalidParameterException {
-            if (limit < 0) {
-                throw new InvalidParameterException("The query limit must be non-negative");
-            }
-
-            mLimit = limit;
-            return this;
-        }
-
-        /**
-         * Sets the type of messages to be returned from the query.
-         *
-         * @param messageType The type of message to be returned.
-         * @return The same instance of the builder to chain parameters.
-         * @see RcsMessageQueryParams#MESSAGE_TYPE_INCOMING
-         * @see RcsMessageQueryParams#MESSAGE_TYPE_OUTGOING
-         */
-        @CheckResult
-        public Builder setMessageType(int messageType) {
-            mMessageType = messageType;
-            return this;
-        }
-
-        /**
-         * Sets whether file transfer messages should be included in the query result or not.
-         *
-         * @param fileTransferPresence Whether file transfers should be included in the result
-         * @return The same instance of the builder to chain parameters.
-         * @see RcsMessageQueryParams#MESSAGES_WITH_FILE_TRANSFERS
-         * @see RcsMessageQueryParams#MESSAGES_WITHOUT_FILE_TRANSFERS
-         */
-        @CheckResult
-        public Builder setFileTransferPresence(int fileTransferPresence) {
-            mFileTransferPresence = fileTransferPresence;
-            return this;
-        }
-
-        /**
-         * Sets an SQL-inspired "like" clause to match with messages. Using a percent sign ('%')
-         * wildcard matches any sequence of zero or more characters. Using an underscore ('_')
-         * wildcard matches any single character. Not using any wildcards would only perform a
-         * string match. The input string is case-insensitive.
-         *
-         * The input "Wh%" would match messages "who", "where" and "what", while the input "Wh_"
-         * would only match "who"
-         *
-         * @param messageLike The "like" clause for matching {@link RcsMessage}s.
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setMessageLike(String messageLike) {
-            mMessageLike = messageLike;
-            return this;
-        }
-
-        /**
-         * Sets the property where the results should be sorted against. Defaults to
-         * {@link RcsMessageQueryParams.SortingProperty#SORT_BY_CREATION_ORDER}
-         *
-         * @param sortingProperty against which property the results should be sorted
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setSortProperty(@SortingProperty int sortingProperty) {
-            mSortingProperty = sortingProperty;
-            return this;
-        }
-
-        /**
-         * Sets whether the results should be sorted ascending or descending
-         *
-         * @param isAscending whether the results should be sorted ascending
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setSortDirection(boolean isAscending) {
-            mIsAscending = isAscending;
-            return this;
-        }
-
-        /**
-         * Limits the results to the given thread.
-         *
-         * @param thread the {@link RcsThread} that results should be limited to. If set to
-         *               {@code null}, messages on all threads will be queried
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setThread(@Nullable RcsThread thread) {
-            if (thread == null) {
-                mThreadId = THREAD_ID_NOT_SET;
-            } else {
-                mThreadId = thread.getThreadId();
-            }
-            return this;
-        }
-
-        /**
-         * Builds the {@link RcsMessageQueryParams} to use in
-         * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
-         *
-         * @return An instance of {@link RcsMessageQueryParams} to use with the message
-         * query.
-         */
-        public RcsMessageQueryParams build() {
-            return new RcsMessageQueryParams(mMessageType, mFileTransferPresence, mMessageLike,
-                    mThreadId, mSortingProperty, mIsAscending, mLimit);
-        }
-    }
-
-    /**
-     * Parcelable boilerplate below.
-     */
-    private RcsMessageQueryParams(Parcel in) {
-        mMessageType = in.readInt();
-        mFileTransferPresence = in.readInt();
-        mMessageLike = in.readString();
-        mSortingProperty = in.readInt();
-        mIsAscending = in.readBoolean();
-        mLimit = in.readInt();
-        mThreadId = in.readInt();
-    }
-
-    public static final @android.annotation.NonNull Creator<RcsMessageQueryParams> CREATOR =
-            new Creator<RcsMessageQueryParams>() {
-                @Override
-                public RcsMessageQueryParams createFromParcel(Parcel in) {
-                    return new RcsMessageQueryParams(in);
-                }
-
-                @Override
-                public RcsMessageQueryParams[] newArray(int size) {
-                    return new RcsMessageQueryParams[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mMessageType);
-        dest.writeInt(mFileTransferPresence);
-        dest.writeString(mMessageLike);
-        dest.writeInt(mSortingProperty);
-        dest.writeBoolean(mIsAscending);
-        dest.writeInt(mLimit);
-        dest.writeInt(mThreadId);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
deleted file mode 100644
index 36bb78a..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.MESSAGE_TYPE_INCOMING;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * The result of a {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
- * call. This class allows getting the token for querying the next batch of messages in order to
- * prevent handling large amounts of data at once.
- *
- * @hide
- */
-public final class RcsMessageQueryResult {
-    private final RcsControllerCall mRcsControllerCall;
-    private final RcsMessageQueryResultParcelable mRcsMessageQueryResultParcelable;
-
-    RcsMessageQueryResult(RcsControllerCall rcsControllerCall,
-            RcsMessageQueryResultParcelable rcsMessageQueryResultParcelable) {
-        mRcsControllerCall = rcsControllerCall;
-        mRcsMessageQueryResultParcelable = rcsMessageQueryResultParcelable;
-    }
-
-    /**
-     * Returns a token to call
-     * {@link RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)}
-     * to get the next batch of {@link RcsMessage}s.
-     */
-    @Nullable
-    public RcsQueryContinuationToken getContinuationToken() {
-        return mRcsMessageQueryResultParcelable.mContinuationToken;
-    }
-
-    /**
-     * Returns all the {@link RcsMessage}s in the current query result. Call {@link
-     * RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)} to get the next batch
-     * of {@link RcsMessage}s.
-     */
-    @NonNull
-    public List<RcsMessage> getMessages() {
-        return mRcsMessageQueryResultParcelable.mMessageTypeIdPairs.stream()
-                .map(typeIdPair -> typeIdPair.getType() == MESSAGE_TYPE_INCOMING
-                        ? new RcsIncomingMessage(mRcsControllerCall, typeIdPair.getId())
-                        : new RcsOutgoingMessage(mRcsControllerCall, typeIdPair.getId()))
-                .collect(Collectors.toList());
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResultParcelable.aidl b/telephony/java/android/telephony/ims/RcsMessageQueryResultParcelable.aidl
deleted file mode 100644
index 86928bf..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageQueryResultParcelable.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsMessageQueryResultParcelable;
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResultParcelable.java b/telephony/java/android/telephony/ims/RcsMessageQueryResultParcelable.java
deleted file mode 100644
index 4972f9b..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageQueryResultParcelable.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.ims.RcsTypeIdPair;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsMessageQueryResultParcelable implements Parcelable {
-    // The token to continue the query to get the next batch of results
-    final RcsQueryContinuationToken mContinuationToken;
-    // The message type and message ID pairs for all the messages in this query result
-    final List<RcsTypeIdPair> mMessageTypeIdPairs;
-
-    public RcsMessageQueryResultParcelable(
-            RcsQueryContinuationToken continuationToken,
-            List<RcsTypeIdPair> messageTypeIdPairs) {
-        mContinuationToken = continuationToken;
-        mMessageTypeIdPairs = messageTypeIdPairs;
-    }
-
-    private RcsMessageQueryResultParcelable(Parcel in) {
-        mContinuationToken = in.readParcelable(
-                RcsQueryContinuationToken.class.getClassLoader());
-
-        mMessageTypeIdPairs = new ArrayList<>();
-        in.readTypedList(mMessageTypeIdPairs, RcsTypeIdPair.CREATOR);
-    }
-
-    public static final Creator<RcsMessageQueryResultParcelable> CREATOR =
-            new Creator<RcsMessageQueryResultParcelable>() {
-                @Override
-                public RcsMessageQueryResultParcelable createFromParcel(Parcel in) {
-                    return new RcsMessageQueryResultParcelable(in);
-                }
-
-                @Override
-                public RcsMessageQueryResultParcelable[] newArray(int size) {
-                    return new RcsMessageQueryResultParcelable[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(mContinuationToken, flags);
-        dest.writeTypedList(mMessageTypeIdPairs);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl b/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl
deleted file mode 100644
index 99b8eb7..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsMessageSnippet;
diff --git a/telephony/java/android/telephony/ims/RcsMessageSnippet.java b/telephony/java/android/telephony/ims/RcsMessageSnippet.java
deleted file mode 100644
index 8103160..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageSnippet.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.ims.RcsMessage.RcsMessageStatus;
-
-/**
- * An immutable summary of the latest {@link RcsMessage} on an {@link RcsThread}
- *
- * @hide
- */
-public final class RcsMessageSnippet implements Parcelable {
-    private final String mText;
-    private final @RcsMessageStatus int mStatus;
-    private final long mTimestamp;
-
-    /**
-     * @hide
-     */
-    public RcsMessageSnippet(String text, @RcsMessageStatus int status, long timestamp) {
-        mText = text;
-        mStatus = status;
-        mTimestamp = timestamp;
-    }
-
-    /**
-     * @return Returns the text of the {@link RcsMessage} with highest origination timestamp value
-     * (i.e. latest) in this thread
-     */
-    @Nullable
-    public String getSnippetText() {
-        return mText;
-    }
-
-    /**
-     * @return Returns the status of the {@link RcsMessage} with highest origination timestamp value
-     * (i.e. latest) in this thread
-     */
-    public @RcsMessageStatus int getSnippetStatus() {
-        return mStatus;
-    }
-
-    /**
-     * @return Returns the timestamp of the {@link RcsMessage} with highest origination timestamp
-     * value (i.e. latest) in this thread
-     */
-    public long getSnippetTimestamp() {
-        return mTimestamp;
-    }
-
-    private RcsMessageSnippet(Parcel in) {
-        mText = in.readString();
-        mStatus = in.readInt();
-        mTimestamp = in.readLong();
-    }
-
-    public static final @android.annotation.NonNull Creator<RcsMessageSnippet> CREATOR =
-            new Creator<RcsMessageSnippet>() {
-                @Override
-                public RcsMessageSnippet createFromParcel(Parcel in) {
-                    return new RcsMessageSnippet(in);
-                }
-
-                @Override
-                public RcsMessageSnippet[] newArray(int size) {
-                    return new RcsMessageSnippet[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mText);
-        dest.writeInt(mStatus);
-        dest.writeLong(mTimestamp);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageStoreException.java b/telephony/java/android/telephony/ims/RcsMessageStoreException.java
deleted file mode 100644
index 3b3fcf2..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageStoreException.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-/**
- * An exception that happened on {@link RcsMessageStore} or one of the derived storage classes in
- * {@link android.telephony.ims}
- *
- * @hide
- */
-public class RcsMessageStoreException extends Exception {
-
-    /**
-     * Constructs an {@link RcsMessageStoreException} with the specified detail message.
-     * @param message The detail message
-     * @see Throwable#getMessage()
-     */
-    public RcsMessageStoreException(String message) {
-        super(message);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
deleted file mode 100644
index 7080ec6..0000000
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.WorkerThread;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This is a single instance of a message sent over RCS.
- *
- * @hide
- */
-public class RcsOutgoingMessage extends RcsMessage {
-    RcsOutgoingMessage(RcsControllerCall rcsControllerCall, int id) {
-        super(rcsControllerCall, id);
-    }
-
-    /**
-     * @return Returns the {@link RcsOutgoingMessageDelivery}s associated with this message. Please
-     * note that the deliveries returned for the {@link RcsOutgoingMessage} may not always match the
-     * {@link RcsParticipant}s on the {@link RcsGroupThread} as the group recipients may have
-     * changed.
-     * @throws RcsMessageStoreException if the outgoing deliveries could not be read from storage.
-     */
-    @NonNull
-    @WorkerThread
-    public List<RcsOutgoingMessageDelivery> getOutgoingDeliveries()
-            throws RcsMessageStoreException {
-        int[] deliveryParticipants;
-        List<RcsOutgoingMessageDelivery> messageDeliveries = new ArrayList<>();
-
-        deliveryParticipants = mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getMessageRecipients(mId, callingPackage));
-
-        if (deliveryParticipants != null) {
-            for (Integer deliveryParticipant : deliveryParticipants) {
-                messageDeliveries.add(new RcsOutgoingMessageDelivery(
-                        mRcsControllerCall, deliveryParticipant, mId));
-            }
-        }
-
-        return messageDeliveries;
-    }
-
-    /**
-     * @return Returns {@code false} as this is not an incoming message.
-     */
-    @Override
-    public boolean isIncoming() {
-        return false;
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl
deleted file mode 100644
index 0c38d9f..0000000
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsOutgoingMessageCreationParams;
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
deleted file mode 100644
index c001ffb..0000000
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * {@link RcsOutgoingMessageCreationParams} is a collection of parameters that should be passed
- * into {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to generate an
- * {@link RcsOutgoingMessage} on that {@link RcsThread}
- *
- * @hide
- */
-public final class RcsOutgoingMessageCreationParams extends RcsMessageCreationParams
-        implements Parcelable {
-    /**
-     * A builder to instantiate and persist an {@link RcsOutgoingMessage}
-     */
-    public static class Builder extends RcsMessageCreationParams.Builder {
-
-        /**
-         * Creates a new {@link Builder} to create an instance of
-         * {@link RcsOutgoingMessageCreationParams}.
-         *
-         * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination
-         *                             timestamp value in milliseconds passed after midnight,
-         *                             January 1, 1970 UTC
-         * @param subscriptionId The subscription ID that was used to send or receive this
-         *                       {@link RcsMessage}
-         * @see android.telephony.SubscriptionInfo#getSubscriptionId()
-         */
-        public Builder(long originationTimestamp, int subscriptionId) {
-            super(originationTimestamp, subscriptionId);
-        }
-
-        /**
-         * Creates configuration parameters for a new message.
-         */
-        public RcsOutgoingMessageCreationParams build() {
-            return new RcsOutgoingMessageCreationParams(this);
-        }
-    }
-
-    private RcsOutgoingMessageCreationParams(Builder builder) {
-        super(builder);
-    }
-
-    private RcsOutgoingMessageCreationParams(Parcel in) {
-        super(in);
-    }
-
-    public static final @NonNull Creator<RcsOutgoingMessageCreationParams> CREATOR =
-            new Creator<RcsOutgoingMessageCreationParams>() {
-                @Override
-                public RcsOutgoingMessageCreationParams createFromParcel(Parcel in) {
-                    return new RcsOutgoingMessageCreationParams(in);
-                }
-
-                @Override
-                public RcsOutgoingMessageCreationParams[] newArray(int size) {
-                    return new RcsOutgoingMessageCreationParams[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
deleted file mode 100644
index df4a3e4..0000000
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
+++ /dev/null
@@ -1,140 +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.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.WorkerThread;
-
-/**
- * This class holds the delivery information of an {@link RcsOutgoingMessage} for each
- * {@link RcsParticipant} that the message was intended for.
- *
- * @hide
- */
-public class RcsOutgoingMessageDelivery {
-    private final RcsControllerCall mRcsControllerCall;
-    // The participant that this delivery is intended for
-    private final int mRecipientId;
-    // The message this delivery is associated with
-    private final int mRcsOutgoingMessageId;
-
-    /**
-     * Constructor to be used with RcsOutgoingMessage.getDelivery()
-     *
-     * @hide
-     */
-    RcsOutgoingMessageDelivery(
-            RcsControllerCall rcsControllerCall, int recipientId, int messageId) {
-        mRcsControllerCall = rcsControllerCall;
-        mRecipientId = recipientId;
-        mRcsOutgoingMessageId = messageId;
-    }
-
-    /**
-     * Sets the delivery time of this outgoing delivery and persists into storage.
-     *
-     * @param deliveredTimestamp The timestamp to set to delivery. It is defined as milliseconds
-     *                           passed after midnight, January 1, 1970 UTC
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setDeliveredTimestamp(long deliveredTimestamp) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setOutgoingDeliveryDeliveredTimestamp(
-                        mRcsOutgoingMessageId, mRecipientId, deliveredTimestamp, callingPackage));
-    }
-
-    /**
-     * @return Returns the delivered timestamp of the associated message to the associated
-     * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC.
-     * Returns 0 if the {@link RcsOutgoingMessage} is not delivered yet.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public long getDeliveredTimestamp() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getOutgoingDeliveryDeliveredTimestamp(
-                        mRcsOutgoingMessageId, mRecipientId, callingPackage));
-    }
-
-    /**
-     * Sets the seen time of this outgoing delivery and persists into storage.
-     *
-     * @param seenTimestamp The timestamp to set to delivery. It is defined as milliseconds
-     *                      passed after midnight, January 1, 1970 UTC
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setSeenTimestamp(long seenTimestamp) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setOutgoingDeliverySeenTimestamp(
-                        mRcsOutgoingMessageId, mRecipientId, seenTimestamp, callingPackage));
-    }
-
-    /**
-     * @return Returns the seen timestamp of the associated message by the associated
-     * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC.
-     * Returns 0 if the {@link RcsOutgoingMessage} is not seen yet.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public long getSeenTimestamp() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getOutgoingDeliverySeenTimestamp(
-                        mRcsOutgoingMessageId, mRecipientId, callingPackage));
-    }
-
-    /**
-     * Sets the status of this outgoing delivery and persists into storage.
-     *
-     * @param status The status of the associated {@link RcsMessage}s delivery to the associated
-     *               {@link RcsParticipant}
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setStatus(@RcsMessage.RcsMessageStatus int status) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setOutgoingDeliveryStatus(
-                        mRcsOutgoingMessageId, mRecipientId, status, callingPackage));
-    }
-
-    /**
-     * @return Returns the status of this outgoing delivery.
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @WorkerThread
-    public @RcsMessage.RcsMessageStatus int getStatus() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getOutgoingDeliveryStatus(mRcsOutgoingMessageId,
-                        mRecipientId, callingPackage));
-    }
-
-    /**
-     * @return Returns the recipient associated with this delivery.
-     */
-    @NonNull
-    public RcsParticipant getRecipient() {
-        return new RcsParticipant(mRcsControllerCall, mRecipientId);
-    }
-
-    /**
-     * @return Returns the {@link RcsOutgoingMessage} associated with this delivery.
-     */
-    @NonNull
-    public RcsOutgoingMessage getMessage() {
-        return new RcsOutgoingMessage(mRcsControllerCall, mRcsOutgoingMessageId);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java
deleted file mode 100644
index 8512e96..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipant.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.Nullable;
-import android.annotation.WorkerThread;
-
-/**
- * RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s.
- *
- * @hide
- */
-public class RcsParticipant {
-    private final RcsControllerCall mRcsControllerCall;
-    // The row ID of this participant in the database
-    private final int mId;
-
-    /**
-     * Constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
-     * to create instances of participants. This is not meant to be part of the SDK.
-     *
-     * @hide
-     */
-    public RcsParticipant(RcsControllerCall rcsControllerCall, int id) {
-        mRcsControllerCall = rcsControllerCall;
-        mId = id;
-    }
-
-    /**
-     * @return Returns the canonical address (i.e. normalized phone number) for this
-     * {@link RcsParticipant}
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @Nullable
-    @WorkerThread
-    public String getCanonicalAddress() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getRcsParticipantCanonicalAddress(mId,
-                        callingPackage));
-    }
-
-    /**
-     * @return Returns the alias for this {@link RcsParticipant}. Alias is usually the real name of
-     * the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal Profile Service
-     * Definition Document)
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @Nullable
-    @WorkerThread
-    public String getAlias() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getRcsParticipantAlias(mId, callingPackage));
-    }
-
-    /**
-     * Sets the alias for this {@link RcsParticipant} and persists it in storage. Alias is usually
-     * the real name of the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal
-     * Profile Service Definition Document)
-     *
-     * @param alias The alias to set to.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setAlias(String alias) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setRcsParticipantAlias(mId, alias, callingPackage));
-    }
-
-    /**
-     * @return Returns the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for
-     * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device
-     * API 1.6 Specification)
-     * @throws RcsMessageStoreException if the value could not be read from the storage
-     */
-    @Nullable
-    @WorkerThread
-    public String getContactId() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getRcsParticipantContactId(mId, callingPackage));
-    }
-
-    /**
-     * Sets the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for
-     * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device
-     * API 1.6 Specification)
-     *
-     * @param contactId The contact ID to set to.
-     * @throws RcsMessageStoreException if the value could not be persisted into storage
-     */
-    @WorkerThread
-    public void setContactId(String contactId) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.setRcsParticipantContactId(mId, contactId,
-                        callingPackage));
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!(obj instanceof RcsParticipant)) {
-            return false;
-        }
-        RcsParticipant other = (RcsParticipant) obj;
-
-        return mId == other.mId;
-    }
-
-    @Override
-    public int hashCode() {
-        return mId;
-    }
-
-    /**
-     * Returns the row id of this participant. This is not meant to be part of the SDK
-     *
-     * @hide
-     */
-    public int getId() {
-        return mId;
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
deleted file mode 100644
index 865bc05..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-/**
- * An event that indicates an {@link RcsParticipant}'s alias was changed. Please see US18-2 - GSMA
- * RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @hide
- */
-public final class RcsParticipantAliasChangedEvent extends RcsEvent {
-    // The participant that changed their alias
-    private final RcsParticipant mParticipant;
-    // The new alias of the above participant
-    private final String mNewAlias;
-
-    /**
-     * Creates a new {@link RcsParticipantAliasChangedEvent}. This event is not persisted into
-     * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
-     *
-     * @param timestamp The timestamp of when this event happened, in milliseconds passed after
-     *                  midnight, January 1st, 1970 UTC
-     * @param participant The {@link RcsParticipant} that got their alias changed
-     * @param newAlias The new alias the {@link RcsParticipant} has.
-     * @see RcsMessageStore#persistRcsEvent(RcsEvent)
-     */
-    public RcsParticipantAliasChangedEvent(long timestamp, @NonNull RcsParticipant participant,
-            @Nullable String newAlias) {
-        super(timestamp);
-        mParticipant = participant;
-        mNewAlias = newAlias;
-    }
-
-    /**
-     * @return Returns the {@link RcsParticipant} whose alias was changed.
-     */
-    @NonNull
-    public RcsParticipant getParticipant() {
-        return mParticipant;
-    }
-
-    /**
-     * @return Returns the alias of the associated {@link RcsParticipant} after this event happened
-     */
-    @Nullable
-    public String getNewAlias() {
-        return mNewAlias;
-    }
-
-    /**
-     * Persists the event to the data store.
-     *
-     * @hide - not meant for public use.
-     */
-    @Override
-    void persist(RcsControllerCall rcsControllerCall) throws RcsMessageStoreException {
-        rcsControllerCall.call((iRcs, callingPackage) -> iRcs.createParticipantAliasChangedEvent(
-                getTimestamp(), getParticipant().getId(), getNewAlias(), callingPackage));
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEventDescriptor.aidl
deleted file mode 100644
index 64fe3b8..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEventDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsParticipantAliasChangedEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEventDescriptor.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEventDescriptor.java
deleted file mode 100644
index 43b918c..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEventDescriptor.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsParticipantAliasChangedEventDescriptor extends RcsEventDescriptor {
-    // The ID of the participant that changed their alias
-    protected int mParticipantId;
-    // The new alias of the above participant
-    protected String mNewAlias;
-
-    public RcsParticipantAliasChangedEventDescriptor(long timestamp, int participantId,
-            @Nullable String newAlias) {
-        super(timestamp);
-        mParticipantId = participantId;
-        mNewAlias = newAlias;
-    }
-
-    @Override
-    @VisibleForTesting(visibility = PROTECTED)
-    public RcsParticipantAliasChangedEvent createRcsEvent(RcsControllerCall rcsControllerCall) {
-        return new RcsParticipantAliasChangedEvent(
-                mTimestamp, new RcsParticipant(rcsControllerCall, mParticipantId), mNewAlias);
-    }
-
-    public static final @NonNull Creator<RcsParticipantAliasChangedEventDescriptor> CREATOR =
-            new Creator<RcsParticipantAliasChangedEventDescriptor>() {
-                @Override
-                public RcsParticipantAliasChangedEventDescriptor createFromParcel(Parcel in) {
-                    return new RcsParticipantAliasChangedEventDescriptor(in);
-                }
-
-                @Override
-                public RcsParticipantAliasChangedEventDescriptor[] newArray(int size) {
-                    return new RcsParticipantAliasChangedEventDescriptor[size];
-                }
-            };
-
-    protected RcsParticipantAliasChangedEventDescriptor(Parcel in) {
-        super(in);
-        mNewAlias = in.readString();
-        mParticipantId = in.readInt();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        super.writeToParcel(dest, flags);
-        dest.writeString(mNewAlias);
-        dest.writeInt(mParticipantId);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl
deleted file mode 100644
index b7c0f93..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsParticipantQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
deleted file mode 100644
index 21107a2..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.CheckResult;
-import android.annotation.IntDef;
-import android.annotation.IntRange;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.security.InvalidParameterException;
-
-/**
- * The parameters to pass into
- * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} in order to select a
- * subset of {@link RcsThread}s present in the message store.
- *
- * @hide
- */
-public final class RcsParticipantQueryParams implements Parcelable {
-    /**
-     * Flag to set with {@link Builder#setSortProperty(int)} to sort the results in the order of
-     * creation time for faster query results
-     */
-    public static final int SORT_BY_CREATION_ORDER = 0;
-
-    /**
-     * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the
-     * {@link RcsParticipant} aliases
-     */
-    public static final int SORT_BY_ALIAS = 1;
-
-    /**
-     * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the
-     * {@link RcsParticipant} canonical addresses
-     */
-    public static final int SORT_BY_CANONICAL_ADDRESS = 2;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_ALIAS, SORT_BY_CANONICAL_ADDRESS})
-    public @interface SortingProperty {
-    }
-
-    // The SQL "like" statement to filter against participant aliases
-    private String mAliasLike;
-    // The SQL "like" statement to filter against canonical addresses
-    private String mCanonicalAddressLike;
-    // The property to sort the result against
-    private @SortingProperty int mSortingProperty;
-    // Whether to sort the result in ascending order
-    private boolean mIsAscending;
-    // The number of results to be returned from the query
-    private int mLimit;
-    // Used to limit the results to participants of a single thread
-    private int mThreadId;
-
-    /**
-     * @hide
-     */
-    public static final String PARTICIPANT_QUERY_PARAMETERS_KEY = "participant_query_parameters";
-
-    RcsParticipantQueryParams(int rcsThreadId, String aliasLike, String canonicalAddressLike,
-            @SortingProperty int sortingProperty, boolean isAscending,
-            int limit) {
-        mThreadId = rcsThreadId;
-        mAliasLike = aliasLike;
-        mCanonicalAddressLike = canonicalAddressLike;
-        mSortingProperty = sortingProperty;
-        mIsAscending = isAscending;
-        mLimit = limit;
-    }
-
-    /**
-     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
-     * the thread that the result query should be limited to.
-     *
-     * As we do not expose any sort of integer ID's to public usage, this should be hidden.
-     *
-     * @hide - not meant for public use
-     */
-    public int getThreadId() {
-        return mThreadId;
-    }
-
-    /**
-     * @return Returns the SQL-inspired "LIKE" clause that will be used to match
-     * {@link RcsParticipant}s with respect to their aliases
-     *
-     * @see RcsParticipant#getAlias()
-     */
-    public String getAliasLike() {
-        return mAliasLike;
-    }
-
-    /**
-     * @return Returns the SQL-inspired "LIKE" clause that will be used to match
-     * {@link RcsParticipant}s with respect to their canonical addresses.
-     *
-     * @see RcsParticipant#getCanonicalAddress()
-     */
-    public String getCanonicalAddressLike() {
-        return mCanonicalAddressLike;
-    }
-
-    /**
-     * @return Returns the number of {@link RcsParticipant}s to be returned from the query. A value
-     * of 0 means there is no set limit.
-     */
-    public int getLimit() {
-        return mLimit;
-    }
-
-    /**
-     * @return Returns the property that will be used to sort the result against.
-     * @see SortingProperty
-     */
-    public int getSortingProperty() {
-        return mSortingProperty;
-    }
-
-    /**
-     * @return Returns {@code true} if the result set will be sorted in ascending order,
-     * {@code false} if it will be sorted in descending order.
-     */
-    public boolean getSortDirection() {
-        return mIsAscending;
-    }
-
-    /**
-     * A helper class to build the {@link RcsParticipantQueryParams}.
-     */
-    public static class Builder {
-        private String mAliasLike;
-        private String mCanonicalAddressLike;
-        private @SortingProperty int mSortingProperty;
-        private boolean mIsAscending;
-        private int mLimit = 100;
-        private int mThreadId;
-
-        /**
-         * Creates a new builder for {@link RcsParticipantQueryParams} to be used in
-         * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
-         */
-        public Builder() {
-            // empty implementation
-        }
-
-        /**
-         * Limits the resulting {@link RcsParticipant}s to only the given {@link RcsThread}
-         *
-         * @param rcsThread The thread that the participants should be searched in.
-         * @return The same {@link Builder} to chain methods.
-         */
-        @CheckResult
-        public Builder setThread(RcsThread rcsThread) {
-            mThreadId = rcsThread.getThreadId();
-            return this;
-        }
-
-        /**
-         * Sets an SQL-inspired "like" clause to match with participant aliases. Using a percent
-         * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore
-         * ('_') wildcard matches any single character. Not using any wildcards would only perform a
-         * string match.The input string is case-insensitive.
-         *
-         * The input "An%e" would match {@link RcsParticipant}s with names Anne, Annie, Antonie,
-         * while the input "An_e" would only match Anne.
-         *
-         * @param likeClause The like clause to use for matching {@link RcsParticipant} aliases.
-         * @return The same {@link Builder} to chain methods
-         */
-        @CheckResult
-        public Builder setAliasLike(String likeClause) {
-            mAliasLike = likeClause;
-            return this;
-        }
-
-        /**
-         * Sets an SQL-inspired "like" clause to match with participant addresses. Using a percent
-         * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore
-         * ('_') wildcard matches any single character. Not using any wildcards would only perform a
-         * string match. The input string is case-insensitive.
-         *
-         * The input "+999%111" would match {@link RcsParticipant}s with addresses like "+9995111"
-         * or "+99955555111", while the input "+999_111" would only match "+9995111".
-         *
-         * @param likeClause The like clause to use for matching {@link RcsParticipant} canonical
-         *                   addresses.
-         * @return The same {@link Builder} to chain methods
-         */
-        @CheckResult
-        public Builder setCanonicalAddressLike(String likeClause) {
-            mCanonicalAddressLike = likeClause;
-            return this;
-        }
-
-        /**
-         * Desired number of threads to be returned from the query. Passing in 0 will return all
-         * existing threads at once. The limit defaults to 100.
-         *
-         * @param limit The number to limit the query result to.
-         * @return The same instance of the builder to chain parameters.
-         * @throws InvalidParameterException If the given limit is negative.
-         */
-        @CheckResult
-        public Builder setResultLimit(@IntRange(from = 0) int limit)
-                throws InvalidParameterException {
-            if (limit < 0) {
-                throw new InvalidParameterException("The query limit must be non-negative");
-            }
-
-            mLimit = limit;
-            return this;
-        }
-
-        /**
-         * Sets the property where the results should be sorted against. Defaults to
-         * {@link RcsParticipantQueryParams.SortingProperty#SORT_BY_CREATION_ORDER}
-         *
-         * @param sortingProperty against which property the results should be sorted
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setSortProperty(@SortingProperty int sortingProperty) {
-            mSortingProperty = sortingProperty;
-            return this;
-        }
-
-        /**
-         * Sets whether the results should be sorted ascending or descending
-         *
-         * @param isAscending whether the results should be sorted ascending
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setSortDirection(boolean isAscending) {
-            mIsAscending = isAscending;
-            return this;
-        }
-
-        /**
-         * Builds the {@link RcsParticipantQueryParams} to use in
-         * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
-         *
-         * @return An instance of {@link RcsParticipantQueryParams} to use with the participant
-         * query.
-         */
-        public RcsParticipantQueryParams build() {
-            return new RcsParticipantQueryParams(mThreadId, mAliasLike, mCanonicalAddressLike,
-                    mSortingProperty, mIsAscending, mLimit);
-        }
-    }
-
-    /**
-     * Parcelable boilerplate below.
-     */
-    private RcsParticipantQueryParams(Parcel in) {
-        mAliasLike = in.readString();
-        mCanonicalAddressLike = in.readString();
-        mSortingProperty = in.readInt();
-        mIsAscending = in.readByte() == 1;
-        mLimit = in.readInt();
-        mThreadId = in.readInt();
-    }
-
-    public static final @android.annotation.NonNull Creator<RcsParticipantQueryParams> CREATOR =
-            new Creator<RcsParticipantQueryParams>() {
-                @Override
-                public RcsParticipantQueryParams createFromParcel(Parcel in) {
-                    return new RcsParticipantQueryParams(in);
-                }
-
-                @Override
-                public RcsParticipantQueryParams[] newArray(int size) {
-                    return new RcsParticipantQueryParams[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mAliasLike);
-        dest.writeString(mCanonicalAddressLike);
-        dest.writeInt(mSortingProperty);
-        dest.writeByte((byte) (mIsAscending ? 1 : 0));
-        dest.writeInt(mLimit);
-        dest.writeInt(mThreadId);
-    }
-
-}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
deleted file mode 100644
index 0721dfd..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * The result of a {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
- * call. This class allows getting the token for querying the next batch of participants in order to
- * prevent handling large amounts of data at once.
- *
- * @hide
- */
-public final class RcsParticipantQueryResult {
-    private final RcsControllerCall mRcsControllerCall;
-    private final RcsParticipantQueryResultParcelable mRcsParticipantQueryResultParcelable;
-
-    RcsParticipantQueryResult(
-            RcsControllerCall rcsControllerCall,
-            RcsParticipantQueryResultParcelable rcsParticipantQueryResultParcelable) {
-        mRcsControllerCall = rcsControllerCall;
-        mRcsParticipantQueryResultParcelable = rcsParticipantQueryResultParcelable;
-    }
-
-    /**
-     * Returns a token to call
-     * {@link RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)}
-     * to get the next batch of {@link RcsParticipant}s.
-     */
-    @Nullable
-    public RcsQueryContinuationToken getContinuationToken() {
-        return mRcsParticipantQueryResultParcelable.mContinuationToken;
-    }
-
-    /**
-     * Returns all the {@link RcsParticipant}s in the current query result. Call {@link
-     * RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)} to get the next
-     * batch of {@link RcsParticipant}s.
-     */
-    @NonNull
-    public List<RcsParticipant> getParticipants() {
-        return mRcsParticipantQueryResultParcelable.mParticipantIds.stream()
-                .map(participantId -> new RcsParticipant(mRcsControllerCall, participantId))
-                .collect(Collectors.toList());
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResultParcelable.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryResultParcelable.aidl
deleted file mode 100644
index 54c72e7..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryResultParcelable.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsParticipantQueryResultParcelable;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResultParcelable.java b/telephony/java/android/telephony/ims/RcsParticipantQueryResultParcelable.java
deleted file mode 100644
index 239b0e9..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryResultParcelable.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide
- */
-public final class RcsParticipantQueryResultParcelable implements Parcelable {
-    final RcsQueryContinuationToken mContinuationToken;
-    final List<Integer> mParticipantIds;
-
-    public RcsParticipantQueryResultParcelable(
-            RcsQueryContinuationToken continuationToken,
-            List<Integer> participantIds) {
-        mContinuationToken = continuationToken;
-        mParticipantIds = participantIds;
-    }
-
-    private RcsParticipantQueryResultParcelable(Parcel in) {
-        mContinuationToken = in.readParcelable(RcsQueryContinuationToken.class.getClassLoader());
-        mParticipantIds = new ArrayList<>();
-        in.readList(mParticipantIds, Integer.class.getClassLoader());
-    }
-
-    public static final Parcelable.Creator<RcsParticipantQueryResultParcelable> CREATOR =
-            new Parcelable.Creator<RcsParticipantQueryResultParcelable>() {
-                @Override
-                public RcsParticipantQueryResultParcelable createFromParcel(Parcel in) {
-                    return new RcsParticipantQueryResultParcelable(in);
-                }
-
-                @Override
-                public RcsParticipantQueryResultParcelable[] newArray(int size) {
-                    return new RcsParticipantQueryResultParcelable[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(mContinuationToken, flags);
-        dest.writeList(mParticipantIds);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl
deleted file mode 100644
index 319379a..0000000
--- a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsQueryContinuationToken;
diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
deleted file mode 100644
index 982263a..0000000
--- a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.IntDef;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A token for enabling continuation queries. Instances are acquired through
- * {@code getContinuationToken} on result objects after initial query is done.
- *
- * @see RcsEventQueryResult#getContinuationToken()
- * @see RcsMessageQueryResult#getContinuationToken()
- * @see RcsParticipantQueryResult#getContinuationToken()
- * @see RcsThreadQueryResult#getContinuationToken()
- *
- * @hide
- */
-public final class RcsQueryContinuationToken implements Parcelable {
-    /**
-     * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
-     * {@link RcsEvent} queries
-     */
-    public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0;
-
-    /**
-     * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
-     * {@link RcsMessage} queries
-     */
-    public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1;
-
-    /**
-     * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
-     * {@link RcsParticipant} queries
-     */
-    public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2;
-
-    /**
-     * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
-     * {@link RcsThread} queries
-     */
-    public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3;
-
-    /**
-     * @hide - not meant for public use
-     */
-    public static final String QUERY_CONTINUATION_TOKEN = "query_continuation_token";
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({EVENT_QUERY_CONTINUATION_TOKEN_TYPE, MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE,
-        PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, THREAD_QUERY_CONTINUATION_TOKEN_TYPE})
-    public @interface ContinuationTokenType {}
-
-    // The type of query this token should allow to continue
-    private @ContinuationTokenType int mQueryType;
-    // The raw query string for the initial query
-    private final String mRawQuery;
-    // The number of results that is returned with each query
-    private final int mLimit;
-    // The offset value that this query should start the query from
-    private int mOffset;
-
-    /**
-     * @hide
-     */
-    public RcsQueryContinuationToken(@ContinuationTokenType int queryType, String rawQuery,
-            int limit, int offset) {
-        mQueryType = queryType;
-        mRawQuery = rawQuery;
-        mLimit = limit;
-        mOffset = offset;
-    }
-
-    /**
-     * Returns the original raw query used on {@link com.android.providers.telephony.RcsProvider}
-     * @hide
-     */
-    public String getRawQuery() {
-        return mRawQuery;
-    }
-
-    /**
-     * Returns which index this continuation query should start from
-     * @hide
-     */
-    public int getOffset() {
-        return mOffset;
-    }
-
-    /**
-     * Increments the offset by the amount of result rows returned with the continuation query for
-     * the next query.
-     * @hide
-     */
-    public void incrementOffset() {
-        mOffset += mLimit;
-    }
-
-    /**
-     * Returns the type of query that this {@link RcsQueryContinuationToken} is intended to be used
-     * to continue.
-     */
-    public @ContinuationTokenType int getQueryType() {
-        return mQueryType;
-    }
-
-    private RcsQueryContinuationToken(Parcel in) {
-        mQueryType = in.readInt();
-        mRawQuery = in.readString();
-        mLimit = in.readInt();
-        mOffset = in.readInt();
-    }
-
-    public static final @android.annotation.NonNull Creator<RcsQueryContinuationToken> CREATOR =
-            new Creator<RcsQueryContinuationToken>() {
-                @Override
-                public RcsQueryContinuationToken createFromParcel(Parcel in) {
-                    return new RcsQueryContinuationToken(in);
-                }
-
-                @Override
-                public RcsQueryContinuationToken[] newArray(int size) {
-                    return new RcsQueryContinuationToken[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mQueryType);
-        dest.writeString(mRawQuery);
-        dest.writeInt(mLimit);
-        dest.writeInt(mOffset);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java
deleted file mode 100644
index efb2cca..0000000
--- a/telephony/java/android/telephony/ims/RcsThread.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1;
-import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_GROUP;
-
-import android.annotation.NonNull;
-import android.annotation.WorkerThread;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * RcsThread represents a single RCS conversation thread. It holds messages that were sent and
- * received and events that occurred on that thread.
- *
- * @hide
- */
-public abstract class RcsThread {
-    /**
-     * The rcs_participant_thread_id that represents this thread in the database
-     *
-     * @hide
-     */
-    protected int mThreadId;
-
-    /**
-     * @hide
-     */
-    protected final RcsControllerCall mRcsControllerCall;
-
-    /**
-     * @hide
-     */
-    protected RcsThread(RcsControllerCall rcsControllerCall, int threadId) {
-        mThreadId = threadId;
-        mRcsControllerCall = rcsControllerCall;
-    }
-
-    /**
-     * @return Returns the summary of the latest message in this {@link RcsThread} packaged in an
-     * {@link RcsMessageSnippet} object
-     */
-    @WorkerThread
-    @NonNull
-    public RcsMessageSnippet getSnippet() throws RcsMessageStoreException {
-        return mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.getMessageSnippet(mThreadId, callingPackage));
-    }
-
-    /**
-     * Adds a new {@link RcsIncomingMessage} to this RcsThread and persists it in storage.
-     *
-     * @throws RcsMessageStoreException if the message could not be persisted into storage.
-     */
-    @WorkerThread
-    @NonNull
-    public RcsIncomingMessage addIncomingMessage(
-            @NonNull RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams)
-            throws RcsMessageStoreException {
-        int messageId = mRcsControllerCall.call(
-                (iRcs, callingPackage) -> iRcs.addIncomingMessage(mThreadId,
-                        rcsIncomingMessageCreationParams, callingPackage));
-        return new RcsIncomingMessage(mRcsControllerCall, messageId);
-    }
-
-    /**
-     * Adds a new {@link RcsOutgoingMessage} to this RcsThread and persists it in storage.
-     *
-     * @throws RcsMessageStoreException if the message could not be persisted into storage.
-     */
-    @WorkerThread
-    @NonNull
-    public RcsOutgoingMessage addOutgoingMessage(
-            @NonNull RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParams)
-            throws RcsMessageStoreException {
-        int messageId = mRcsControllerCall.call((iRcs, callingPackage) -> iRcs.addOutgoingMessage(
-                mThreadId, rcsOutgoingMessageCreationParams, callingPackage));
-
-        return new RcsOutgoingMessage(mRcsControllerCall, messageId);
-    }
-
-    /**
-     * Deletes an {@link RcsMessage} from this RcsThread and updates the storage.
-     *
-     * @param rcsMessage The message to delete from the thread
-     * @throws RcsMessageStoreException if the message could not be deleted
-     */
-    @WorkerThread
-    public void deleteMessage(@NonNull RcsMessage rcsMessage) throws RcsMessageStoreException {
-        mRcsControllerCall.callWithNoReturn(
-                (iRcs, callingPackage) -> iRcs.deleteMessage(rcsMessage.getId(),
-                        rcsMessage.isIncoming(), mThreadId,
-                        isGroup(), callingPackage));
-    }
-
-    /**
-     * Convenience function for loading all the {@link RcsMessage}s in this {@link RcsThread}. For
-     * a more detailed and paginated query, please use
-     * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
-     *
-     * @return Loads the {@link RcsMessage}s in this thread and returns them in an immutable list.
-     * @throws RcsMessageStoreException if the messages could not be read from the storage
-     */
-    @WorkerThread
-    @NonNull
-    public RcsMessageQueryResult getMessages() throws RcsMessageStoreException {
-        RcsMessageQueryParams queryParams =
-                new RcsMessageQueryParams.Builder().setThread(this).build();
-        return new RcsMessageQueryResult(mRcsControllerCall,
-                mRcsControllerCall.call(
-                        (iRcs, callingPackage) -> iRcs.getMessages(queryParams, callingPackage)));
-    }
-
-    /**
-     * @return Returns whether this is a group thread or not
-     */
-    public abstract boolean isGroup();
-
-    /**
-     * @hide
-     */
-    @VisibleForTesting
-    public int getThreadId() {
-        return mThreadId;
-    }
-
-    /**
-     * @hide
-     */
-    public int getThreadType() {
-        return isGroup() ? THREAD_TYPE_GROUP : THREAD_TYPE_1_TO_1;
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl
deleted file mode 100644
index 3f351dc..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsThreadQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
deleted file mode 100644
index da7cdb0..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.CheckResult;
-import android.annotation.IntDef;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.security.InvalidParameterException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} in
- * order to select a subset of {@link RcsThread}s present in the message store.
- *
- * @hide
- */
-public final class RcsThreadQueryParams implements Parcelable {
-    /**
-     * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make
-     * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} return
-     * {@link RcsGroupThread}s.
-     */
-    public static final int THREAD_TYPE_GROUP = 0x0001;
-
-    /**
-     * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make
-     * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} return
-     * {@link Rcs1To1Thread}s.
-     */
-    public static final int THREAD_TYPE_1_TO_1 = 0x0002;
-
-    // The type of threads to be filtered with the query
-    private final int mThreadType;
-    // The list of participants that are expected in the resulting threads
-    private final List<Integer> mRcsParticipantIds;
-    // The number of RcsThread's that should be returned with this query
-    private final int mLimit;
-    // The property which the result of the query should be sorted against
-    private final @SortingProperty int mSortingProperty;
-    // Whether the sorting should be done in ascending
-    private final boolean mIsAscending;
-
-    /**
-     * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
-     * be sorted in the order of {@link RcsThread} creation time for faster results.
-     */
-    public static final int SORT_BY_CREATION_ORDER = 0;
-
-    /**
-     * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
-     * be sorted according to the timestamp of {@link RcsThread#getSnippet()}
-     */
-    public static final int SORT_BY_TIMESTAMP = 1;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
-    public @interface SortingProperty {
-    }
-
-    /**
-     * @hide
-     */
-    public static final String THREAD_QUERY_PARAMETERS_KEY = "thread_query_parameters";
-
-    RcsThreadQueryParams(int threadType, Set<RcsParticipant> participants,
-            int limit, int sortingProperty, boolean isAscending) {
-        mThreadType = threadType;
-        mRcsParticipantIds = convertParticipantSetToIdList(participants);
-        mLimit = limit;
-        mSortingProperty = sortingProperty;
-        mIsAscending = isAscending;
-    }
-
-    private static List<Integer> convertParticipantSetToIdList(Set<RcsParticipant> participants) {
-        List<Integer> ids = new ArrayList<>(participants.size());
-        for (RcsParticipant participant : participants) {
-            ids.add(participant.getId());
-        }
-        return ids;
-    }
-
-    /**
-     * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
-     * the list of participant IDs.
-     *
-     * As we don't expose any integer ID's to API users, this should stay hidden
-     *
-     * @hide - not meant for public use
-     */
-    public List<Integer> getRcsParticipantsIds() {
-        return Collections.unmodifiableList(mRcsParticipantIds);
-    }
-
-    /**
-     * @return Returns the bitmask flag for types of {@link RcsThread}s that this query should
-     * return.
-     */
-    public int getThreadType() {
-        return mThreadType;
-    }
-
-    /**
-     * @return Returns the number of {@link RcsThread}s to be returned from the query. A value
-     * of 0 means there is no set limit.
-     */
-    public int getLimit() {
-        return mLimit;
-    }
-
-    /**
-     * @return Returns the property that will be used to sort the result against.
-     * @see SortingProperty
-     */
-    public @SortingProperty int getSortingProperty() {
-        return mSortingProperty;
-    }
-
-    /**
-     * @return Returns {@code true} if the result set will be sorted in ascending order,
-     * {@code false} if it will be sorted in descending order.
-     */
-    public boolean getSortDirection() {
-        return mIsAscending;
-    }
-
-    /**
-     * A helper class to build the {@link RcsThreadQueryParams}.
-     */
-    public static class Builder {
-        private int mThreadType;
-        private Set<RcsParticipant> mParticipants;
-        private int mLimit = 100;
-        private @SortingProperty int mSortingProperty;
-        private boolean mIsAscending;
-
-        /**
-         * Constructs a {@link RcsThreadQueryParams.Builder} to help build an
-         * {@link RcsThreadQueryParams}
-         */
-        public Builder() {
-            mParticipants = new HashSet<>();
-        }
-
-        /**
-         * Limits the query to only return group threads.
-         *
-         * @param threadType Whether to limit the query result to group threads.
-         * @return The same instance of the builder to chain parameters.
-         * @see RcsThreadQueryParams#THREAD_TYPE_GROUP
-         * @see RcsThreadQueryParams#THREAD_TYPE_1_TO_1
-         */
-        @CheckResult
-        public Builder setThreadType(int threadType) {
-            mThreadType = threadType;
-            return this;
-        }
-
-        /**
-         * Limits the query to only return threads that contain the given participant. If this
-         * property was not set, participants will not be taken into account while querying for
-         * threads.
-         *
-         * @param participant The participant that must be included in all of the returned threads.
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setParticipant(@NonNull RcsParticipant participant) {
-            mParticipants.add(participant);
-            return this;
-        }
-
-        /**
-         * Limits the query to only return threads that contain the given list of participants. If
-         * this property was not set, participants will not be taken into account while querying
-         * for threads.
-         *
-         * @param participants An iterable list of participants that must be included in all of the
-         *                     returned threads.
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setParticipants(@NonNull List<RcsParticipant> participants) {
-            mParticipants.addAll(participants);
-            return this;
-        }
-
-        /**
-         * Desired number of threads to be returned from the query. Passing in 0 will return all
-         * existing threads at once. The limit defaults to 100.
-         *
-         * @param limit The number to limit the query result to.
-         * @return The same instance of the builder to chain parameters.
-         * @throws InvalidParameterException If the given limit is negative.
-         */
-        @CheckResult
-        public Builder setResultLimit(@IntRange(from = 0) int limit)
-                throws InvalidParameterException {
-            if (limit < 0) {
-                throw new InvalidParameterException("The query limit must be non-negative");
-            }
-
-            mLimit = limit;
-            return this;
-        }
-
-        /**
-         * Sets the property where the results should be sorted against. Defaults to
-         * {@link SortingProperty#SORT_BY_CREATION_ORDER}
-         *
-         * @param sortingProperty whether to sort in ascending order or not
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setSortProperty(@SortingProperty int sortingProperty) {
-            mSortingProperty = sortingProperty;
-            return this;
-        }
-
-        /**
-         * Sets whether the results should be sorted ascending or descending
-         *
-         * @param isAscending whether the results should be sorted ascending
-         * @return The same instance of the builder to chain parameters.
-         */
-        @CheckResult
-        public Builder setSortDirection(boolean isAscending) {
-            mIsAscending = isAscending;
-            return this;
-        }
-
-        /**
-         * Builds the {@link RcsThreadQueryParams} to use in
-         * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)}
-         *
-         * @return An instance of {@link RcsThreadQueryParams} to use with the thread query.
-         */
-        public RcsThreadQueryParams build() {
-            return new RcsThreadQueryParams(mThreadType, mParticipants, mLimit,
-                    mSortingProperty, mIsAscending);
-        }
-    }
-
-    /**
-     * Parcelable boilerplate below.
-     */
-    private RcsThreadQueryParams(Parcel in) {
-        mThreadType = in.readInt();
-        mRcsParticipantIds = new ArrayList<>();
-        in.readList(mRcsParticipantIds, Integer.class.getClassLoader());
-        mLimit = in.readInt();
-        mSortingProperty = in.readInt();
-        mIsAscending = in.readByte() == 1;
-    }
-
-    public static final @android.annotation.NonNull Creator<RcsThreadQueryParams> CREATOR =
-            new Creator<RcsThreadQueryParams>() {
-                @Override
-                public RcsThreadQueryParams createFromParcel(Parcel in) {
-                    return new RcsThreadQueryParams(in);
-                }
-
-                @Override
-                public RcsThreadQueryParams[] newArray(int size) {
-                    return new RcsThreadQueryParams[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mThreadType);
-        dest.writeList(mRcsParticipantIds);
-        dest.writeInt(mLimit);
-        dest.writeInt(mSortingProperty);
-        dest.writeByte((byte) (mIsAscending ? 1 : 0));
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
deleted file mode 100644
index 3de25de..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-
-/**
- * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)}
- * call. This class allows getting the token for querying the next batch of threads in order to
- * prevent handling large amounts of data at once.
- *
- * @hide
- */
-public final class RcsThreadQueryResult {
-    private final RcsControllerCall mRcsControllerCall;
-    private final RcsThreadQueryResultParcelable mRcsThreadQueryResultParcelable;
-
-    RcsThreadQueryResult(RcsControllerCall rcsControllerCall,
-            RcsThreadQueryResultParcelable rcsThreadQueryResultParcelable) {
-        mRcsControllerCall = rcsControllerCall;
-        mRcsThreadQueryResultParcelable = rcsThreadQueryResultParcelable;
-    }
-
-    /**
-     * Returns a token to call
-     * {@link RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)}
-     * to get the next batch of {@link RcsThread}s.
-     */
-    @Nullable
-    public RcsQueryContinuationToken getContinuationToken() {
-        return mRcsThreadQueryResultParcelable.mContinuationToken;
-    }
-
-    /**
-     * Returns all the RcsThreads in the current query result. Call {@link
-     * RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)} to get the next batch of
-     * {@link RcsThread}s.
-     */
-    @NonNull
-    public List<RcsThread> getThreads() {
-        return mRcsThreadQueryResultParcelable.mRcsThreadIds.stream()
-                .map(typeIdPair -> typeIdPair.getType() == THREAD_TYPE_1_TO_1
-                        ? new Rcs1To1Thread(mRcsControllerCall, typeIdPair.getId())
-                        : new RcsGroupThread(mRcsControllerCall, typeIdPair.getId()))
-                .collect(Collectors.toList());
-    }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResultParcelable.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryResultParcelable.aidl
deleted file mode 100644
index 05bd61d..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResultParcelable.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.telephony.ims;
-
-parcelable RcsThreadQueryResultParcelable;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResultParcelable.java b/telephony/java/android/telephony/ims/RcsThreadQueryResultParcelable.java
deleted file mode 100644
index 89dd1d4..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResultParcelable.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.ims.RcsTypeIdPair;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide
- */
-public final class RcsThreadQueryResultParcelable implements Parcelable {
-    final RcsQueryContinuationToken mContinuationToken;
-    final List<RcsTypeIdPair> mRcsThreadIds;
-
-    public RcsThreadQueryResultParcelable(
-            RcsQueryContinuationToken continuationToken,
-            List<RcsTypeIdPair> rcsThreadIds) {
-        mContinuationToken = continuationToken;
-        mRcsThreadIds = rcsThreadIds;
-    }
-
-    private RcsThreadQueryResultParcelable(Parcel in) {
-        mContinuationToken = in.readParcelable(RcsQueryContinuationToken.class.getClassLoader());
-        mRcsThreadIds = new ArrayList<>();
-        in.readList(mRcsThreadIds, RcsTypeIdPair.class.getClassLoader());
-    }
-
-    public static final Parcelable.Creator<RcsThreadQueryResultParcelable> CREATOR =
-            new Parcelable.Creator<RcsThreadQueryResultParcelable>() {
-                @Override
-                public RcsThreadQueryResultParcelable createFromParcel(Parcel in) {
-                    return new RcsThreadQueryResultParcelable(in);
-                }
-
-                @Override
-                public RcsThreadQueryResultParcelable[] newArray(int size) {
-                    return new RcsThreadQueryResultParcelable[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(mContinuationToken, flags);
-        dest.writeList(mRcsThreadIds);
-    }
-}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
index cc2ebb9..36d2067 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
@@ -148,6 +148,12 @@
     void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile);
 
     /**
+     * Notifies the result of transfer request.
+     */
+    void callSessionTransferred();
+    void callSessionTransferFailed(in ImsReasonInfo reasonInfo);
+
+    /**
      * Notifies of a change to the call quality.
      * @param callQuality then updated call quality
      */
diff --git a/telephony/java/android/telephony/ims/aidl/IRcsMessage.aidl b/telephony/java/android/telephony/ims/aidl/IRcsMessage.aidl
deleted file mode 100644
index 0ae6303..0000000
--- a/telephony/java/android/telephony/ims/aidl/IRcsMessage.aidl
+++ /dev/null
@@ -1,266 +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.telephony.ims.aidl;
-
-import android.net.Uri;
-import android.telephony.ims.RcsEventQueryParams;
-import android.telephony.ims.RcsEventQueryResultDescriptor;
-import android.telephony.ims.RcsFileTransferCreationParams;
-import android.telephony.ims.RcsIncomingMessageCreationParams;
-import android.telephony.ims.RcsMessageSnippet;
-import android.telephony.ims.RcsMessageQueryParams;
-import android.telephony.ims.RcsMessageQueryResultParcelable;
-import android.telephony.ims.RcsOutgoingMessageCreationParams;
-import android.telephony.ims.RcsParticipantQueryParams;
-import android.telephony.ims.RcsParticipantQueryResultParcelable;
-import android.telephony.ims.RcsQueryContinuationToken;
-import android.telephony.ims.RcsThreadQueryParams;
-import android.telephony.ims.RcsThreadQueryResultParcelable;
-
-/**
- * RPC definition between RCS storage APIs and phone process.
- * {@hide}
- */
-interface IRcsMessage {
-    /////////////////////////
-    // RcsMessageStore APIs
-    /////////////////////////
-    RcsThreadQueryResultParcelable getRcsThreads(in RcsThreadQueryParams queryParams, String callingPackage);
-
-    RcsThreadQueryResultParcelable getRcsThreadsWithToken(
-        in RcsQueryContinuationToken continuationToken, String callingPackage);
-
-    RcsParticipantQueryResultParcelable getParticipants(in RcsParticipantQueryParams queryParams, String callingPackage);
-
-    RcsParticipantQueryResultParcelable getParticipantsWithToken(
-        in RcsQueryContinuationToken continuationToken, String callingPackage);
-
-    RcsMessageQueryResultParcelable getMessages(in RcsMessageQueryParams queryParams, String callingPackage);
-
-    RcsMessageQueryResultParcelable getMessagesWithToken(
-        in RcsQueryContinuationToken continuationToken, String callingPackage);
-
-    RcsEventQueryResultDescriptor getEvents(in RcsEventQueryParams queryParams, String callingPackage);
-
-    RcsEventQueryResultDescriptor getEventsWithToken(
-        in RcsQueryContinuationToken continuationToken, String callingPackage);
-
-    // returns true if the thread was successfully deleted
-    boolean deleteThread(int threadId, int threadType, String callingPackage);
-
-    // Creates an Rcs1To1Thread and returns its row ID
-    int createRcs1To1Thread(int participantId, String callingPackage);
-
-    // Creates an RcsGroupThread and returns its row ID
-    int createGroupThread(in int[] participantIds, String groupName, in Uri groupIcon, String callingPackage);
-
-    /////////////////////////
-    // RcsThread APIs
-    /////////////////////////
-
-    // Creates a new RcsIncomingMessage on the given thread and returns its row ID
-    int addIncomingMessage(int rcsThreadId,
-            in RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams, String callingPackage);
-
-    // Creates a new RcsOutgoingMessage on the given thread and returns its row ID
-    int addOutgoingMessage(int rcsThreadId,
-            in RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParams, String callingPackage);
-
-    // TODO: modify RcsProvider URI's to allow deleting a message without specifying its thread
-    void deleteMessage(int rcsMessageId, boolean isIncoming, int rcsThreadId, boolean isGroup, String callingPackage);
-
-    RcsMessageSnippet getMessageSnippet(int rcsThreadId, String callingPackage);
-
-    /////////////////////////
-    // Rcs1To1Thread APIs
-    /////////////////////////
-    void set1To1ThreadFallbackThreadId(int rcsThreadId, long fallbackId, String callingPackage);
-
-    long get1To1ThreadFallbackThreadId(int rcsThreadId, String callingPackage);
-
-    int get1To1ThreadOtherParticipantId(int rcsThreadId, String callingPackage);
-
-    /////////////////////////
-    // RcsGroupThread APIs
-    /////////////////////////
-    void setGroupThreadName(int rcsThreadId, String groupName, String callingPackage);
-
-    String getGroupThreadName(int rcsThreadId, String callingPackage);
-
-    void setGroupThreadIcon(int rcsThreadId, in Uri groupIcon, String callingPackage);
-
-    Uri getGroupThreadIcon(int rcsThreadId, String callingPackage);
-
-    void setGroupThreadOwner(int rcsThreadId, int participantId, String callingPackage);
-
-    int getGroupThreadOwner(int rcsThreadId, String callingPackage);
-
-    void setGroupThreadConferenceUri(int rcsThreadId, in Uri conferenceUri, String callingPackage);
-
-    Uri getGroupThreadConferenceUri(int rcsThreadId, String callingPackage);
-
-    void addParticipantToGroupThread(int rcsThreadId, int participantId, String callingPackage);
-
-    void removeParticipantFromGroupThread(int rcsThreadId, int participantId, String callingPackage);
-
-    /////////////////////////
-    // RcsParticipant APIs
-    /////////////////////////
-
-    // Creates a new RcsParticipant and returns its rowId
-    int createRcsParticipant(String canonicalAddress, String alias, String callingPackage);
-
-    String getRcsParticipantCanonicalAddress(int participantId, String callingPackage);
-
-    String getRcsParticipantAlias(int participantId, String callingPackage);
-
-    void setRcsParticipantAlias(int id, String alias, String callingPackage);
-
-    String getRcsParticipantContactId(int participantId, String callingPackage);
-
-    void setRcsParticipantContactId(int participantId, String contactId, String callingPackage);
-
-    /////////////////////////
-    // RcsMessage APIs
-    /////////////////////////
-    void setMessageSubId(int messageId, boolean isIncoming, int subId, String callingPackage);
-
-    int getMessageSubId(int messageId, boolean isIncoming, String callingPackage);
-
-    void setMessageStatus(int messageId, boolean isIncoming, int status, String callingPackage);
-
-    int getMessageStatus(int messageId, boolean isIncoming, String callingPackage);
-
-    void setMessageOriginationTimestamp(int messageId, boolean isIncoming, long originationTimestamp, String callingPackage);
-
-    long getMessageOriginationTimestamp(int messageId, boolean isIncoming, String callingPackage);
-
-    void setGlobalMessageIdForMessage(int messageId, boolean isIncoming, String globalId, String callingPackage);
-
-    String getGlobalMessageIdForMessage(int messageId, boolean isIncoming, String callingPackage);
-
-    void setMessageArrivalTimestamp(int messageId, boolean isIncoming, long arrivalTimestamp, String callingPackage);
-
-    long getMessageArrivalTimestamp(int messageId, boolean isIncoming, String callingPackage);
-
-    void setMessageSeenTimestamp(int messageId, boolean isIncoming, long seenTimestamp, String callingPackage);
-
-    long getMessageSeenTimestamp(int messageId, boolean isIncoming, String callingPackage);
-
-    void setTextForMessage(int messageId, boolean isIncoming, String text, String callingPackage);
-
-    String getTextForMessage(int messageId, boolean isIncoming, String callingPackage);
-
-    void setLatitudeForMessage(int messageId, boolean isIncoming, double latitude, String callingPackage);
-
-    double getLatitudeForMessage(int messageId, boolean isIncoming, String callingPackage);
-
-    void setLongitudeForMessage(int messageId, boolean isIncoming, double longitude, String callingPackage);
-
-    double getLongitudeForMessage(int messageId, boolean isIncoming, String callingPackage);
-
-    // Returns the ID's of the file transfers attached to the given message
-    int[] getFileTransfersAttachedToMessage(int messageId, boolean isIncoming, String callingPackage);
-
-    int getSenderParticipant(int messageId, String callingPackage);
-
-    /////////////////////////
-    // RcsOutgoingMessageDelivery APIs
-    /////////////////////////
-
-    // Returns the participant ID's that this message is intended to be delivered to
-    int[] getMessageRecipients(int messageId, String callingPackage);
-
-    long getOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId, String callingPackage);
-
-    void setOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId, long deliveredTimestamp, String callingPackage);
-
-    long getOutgoingDeliverySeenTimestamp(int messageId, int participantId, String callingPackage);
-
-    void setOutgoingDeliverySeenTimestamp(int messageId, int participantId, long seenTimestamp, String callingPackage);
-
-    int getOutgoingDeliveryStatus(int messageId, int participantId, String callingPackage);
-
-    void setOutgoingDeliveryStatus(int messageId, int participantId, int status, String callingPackage);
-
-    /////////////////////////
-    // RcsFileTransferPart APIs
-    /////////////////////////
-
-    // Performs the initial write to storage and returns the row ID.
-    int storeFileTransfer(int messageId, boolean isIncoming,
-            in RcsFileTransferCreationParams fileTransferCreationParams, String callingPackage);
-
-    void deleteFileTransfer(int partId, String callingPackage);
-
-    void setFileTransferSessionId(int partId, String sessionId, String callingPackage);
-
-    String getFileTransferSessionId(int partId, String callingPackage);
-
-    void setFileTransferContentUri(int partId, in Uri contentUri, String callingPackage);
-
-    Uri getFileTransferContentUri(int partId, String callingPackage);
-
-    void setFileTransferContentType(int partId, String contentType, String callingPackage);
-
-    String getFileTransferContentType(int partId, String callingPackage);
-
-    void setFileTransferFileSize(int partId, long fileSize, String callingPackage);
-
-    long getFileTransferFileSize(int partId, String callingPackage);
-
-    void setFileTransferTransferOffset(int partId, long transferOffset, String callingPackage);
-
-    long getFileTransferTransferOffset(int partId, String callingPackage);
-
-    void setFileTransferStatus(int partId, int transferStatus, String callingPackage);
-
-    int getFileTransferStatus(int partId, String callingPackage);
-
-    void setFileTransferWidth(int partId, int width, String callingPackage);
-
-    int getFileTransferWidth(int partId, String callingPackage);
-
-    void setFileTransferHeight(int partId, int height, String callingPackage);
-
-    int getFileTransferHeight(int partId, String callingPackage);
-
-    void setFileTransferLength(int partId, long length, String callingPackage);
-
-    long getFileTransferLength(int partId, String callingPackage);
-
-    void setFileTransferPreviewUri(int partId, in Uri uri, String callingPackage);
-
-    Uri getFileTransferPreviewUri(int partId, String callingPackage);
-
-    void setFileTransferPreviewType(int partId, String type, String callingPackage);
-
-    String getFileTransferPreviewType(int partId, String callingPackage);
-
-    /////////////////////////
-    // RcsEvent APIs
-    /////////////////////////
-    int createGroupThreadNameChangedEvent(long timestamp, int threadId, int originationParticipantId, String newName, String callingPackage);
-
-    int createGroupThreadIconChangedEvent(long timestamp, int threadId, int originationParticipantId, in Uri newIcon, String callingPackage);
-
-    int createGroupThreadParticipantJoinedEvent(long timestamp, int threadId, int originationParticipantId, int participantId, String callingPackage);
-
-    int createGroupThreadParticipantLeftEvent(long timestamp, int threadId, int originationParticipantId, int participantId, String callingPackage);
-
-    int createParticipantAliasChangedEvent(long timestamp, int participantId, String newAlias, String callingPackage);
-}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
old mode 100644
new mode 100755
index 75bd6a7..06aa642
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -16,6 +16,8 @@
 
 package android.telephony.ims.compat.stub;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Message;
 import android.os.RemoteException;
@@ -197,6 +199,29 @@
     }
 
     /**
+     * Transfer an established call to given number, disconnecting the ongoing call
+     * when the transfer is complete.
+     *
+     * @param number number to transfer the call
+     * @param isConfirmationRequired when {@code true}, then the {@link ImsCallSessionImplBase}
+     * should wait until the transfer has successfully completed before disconnecting the current
+     * {@link ImsCallSessionImplBase}. When {@code false}, the {@link ImsCallSessionImplBase}
+     * should signal the network to perform the transfer, but should immediately disconnect the
+     * call regardless of the outcome of the transfer.
+     */
+    @Override
+    public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+    }
+
+    /**
+     * Transfer an established call to an existing ongoing session.
+     * When the transfer is complete, the current call gets disconnected locally.
+     */
+    @Override
+    public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
+    }
+
+    /**
      * Rejects an incoming call or session update.
      *
      * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
@@ -610,6 +635,17 @@
         }
 
         @Override
+        public void callSessionTransferred() throws RemoteException {
+            mNewListener.callSessionTransferred();
+        }
+
+        @Override
+        public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo)
+                throws RemoteException {
+            mNewListener.callSessionTransferFailed(reasonInfo);
+        }
+
+        @Override
         public void callQualityChanged(CallQuality callQuality) throws RemoteException {
             mNewListener.callQualityChanged(callQuality);
         }
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index e8f69ea..73ba0e3 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -16,6 +16,7 @@
 
 package android.telephony.ims.stub;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.os.Message;
@@ -183,6 +184,18 @@
         }
 
         @Override
+        public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+            ImsCallSessionImplBase.this.transfer(number, isConfirmationRequired);
+        }
+
+        @Override
+        public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
+            ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase();
+            otherSession.setServiceImpl(transferToSession);
+            ImsCallSessionImplBase.this.transfer(otherSession);
+        }
+
+        @Override
         public void terminate(int reason) {
             ImsCallSessionImplBase.this.terminate(reason);
         }
@@ -423,6 +436,26 @@
     }
 
     /**
+     * Transfer an established call to given number
+     *
+     * @param number number to transfer the call
+     * @param isConfirmationRequired if {@code True}, indicates Assured transfer,
+     * if {@code False} it indicates Blind transfer.
+     * @hide
+     */
+    public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+    }
+
+    /**
+     * Transfer an established call to another call session
+     *
+     * @param otherSession The other ImsCallSession to transfer the ongoing session to.
+     * @hide
+     */
+    public void transfer(@NonNull ImsCallSessionImplBase otherSession) {
+    }
+
+    /**
      * Terminates a call.
      *
      * @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
diff --git a/telephony/java/com/android/ims/internal/IImsCallSession.aidl b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
index 15234e5..0466efc 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSession.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
@@ -18,7 +18,6 @@
 
 import android.os.Message;
 import android.telephony.ims.aidl.IImsCallSessionListener;
-
 import android.telephony.ims.ImsCallProfile;
 import android.telephony.ims.ImsStreamMediaProfile;
 import com.android.ims.internal.IImsVideoCallProvider;
@@ -151,6 +150,22 @@
     void reject(int reason);
 
     /**
+     * Transfer an established call to given number
+     *
+     * @param number number to transfer the call
+     * @param isConfirmationRequired if {@code True}, indicates Assured transfer,
+     * if {@code False} it indicates Blind transfer.
+     */
+    void transfer(String number, boolean isConfirmationRequired);
+
+    /**
+     * Transfer an established call to another call session
+     *
+     * @param transferToSession The other ImsCallSession to transfer the ongoing session to.
+     */
+    void consultativeTransfer(in IImsCallSession transferToSession);
+
+    /**
      * Terminates a call.
      *
      * @see Listener#callSessionTerminated
diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
index b33a9f1..1c62cc4 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -184,6 +184,12 @@
     void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile);
 
     /**
+     * Notifies about the response for call transfer request.
+     */
+    void callSessionTransferred();
+
+    void callSessionTransferFailed(in ImsReasonInfo reasonInfo);
+    /**
      * Notifies of a change to the call quality.
      * @param callQuality then updated call quality
      */
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 5c18cdd..0f2cb44 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -39,7 +39,6 @@
 import android.telephony.ModemActivityInfo;
 import android.telephony.NeighboringCellInfo;
 import android.telephony.NetworkScanRequest;
-import android.telephony.PhoneCapability;
 import android.telephony.PhoneNumberRange;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
@@ -1622,16 +1621,6 @@
     void carrierActionResetAll(int subId);
 
     /**
-     * Get aggregated video call data usage since boot.
-     * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
-     *
-     * @param perUidStats True if requesting data usage per uid, otherwise overall usage.
-     * @return Snapshot of video call data usage
-     * @hide
-     */
-    NetworkStats getVtDataUsage(int subId, boolean perUidStats);
-
-    /**
      * Gets the voice call forwarding info {@link CallForwardingInfo}, given the call forward
      * reason.
      *
@@ -1905,17 +1894,12 @@
     /**
      * Return the network selection mode on the subscription with id {@code subId}.
      */
-    int getNetworkSelectionMode(int subId);
+     int getNetworkSelectionMode(int subId);
 
-    /**
-     * Return the PhoneCapability for the device.
-     */
-    PhoneCapability getPhoneCapability(int subId, String callingPackage, String callingFeatureId);
-
-    /**
+     /**
      * Return true if the device is in emergency sms mode, false otherwise.
      */
-    boolean isInEmergencySmsMode();
+     boolean isInEmergencySmsMode();
 
     /**
      * Return the modem radio power state for slot index.
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 39f1fc2..48cb1cd 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -248,10 +248,10 @@
         public int hour;
         public int monthDay;
 
-        /** Month [0-11] */
-        public int month;
+        /** Month in the range 1(Jan) - 12(Dec). */
+        public int monthOrdinal;
 
-        /** Full year. For example, 1970. */
+        /** Full year in the range 1996 - 2095. */
         public int year;
 
         private ZoneId mZoneId;
@@ -269,7 +269,7 @@
             ts.year = year >= 96 ? year + 1900 : year + 2000;
             int month = IccUtils.cdmaBcdByteToInt(data[1]);
             if (month < 1 || month > 12) return null;
-            ts.month = month - 1;
+            ts.monthOrdinal = month;
             int day = IccUtils.cdmaBcdByteToInt(data[2]);
             if (day < 1 || day > 31) return null;
             ts.monthDay = day;
@@ -292,7 +292,7 @@
             int year = localDateTime.getYear();
             if (year < 1996 || year > 2095) return null;
             ts.year = year;
-            ts.month = localDateTime.getMonthValue();
+            ts.monthOrdinal = localDateTime.getMonthValue();
             ts.monthDay = localDateTime.getDayOfMonth();
             ts.hour = localDateTime.getHour();
             ts.minute = localDateTime.getMinute();
@@ -304,7 +304,7 @@
             int year = this.year % 100; // 00 - 99
             ByteArrayOutputStream outStream = new ByteArrayOutputStream(6);
             outStream.write((((year / 10) & 0x0F) << 4) | ((year % 10) & 0x0F));
-            outStream.write((((month / 10) << 4) & 0xF0) | ((month % 10) & 0x0F));
+            outStream.write((((monthOrdinal / 10) << 4) & 0xF0) | ((monthOrdinal % 10) & 0x0F));
             outStream.write((((monthDay / 10) << 4) & 0xF0) | ((monthDay % 10) & 0x0F));
             outStream.write((((hour / 10) << 4) & 0xF0) | ((hour % 10) & 0x0F));
             outStream.write((((minute / 10) << 4) & 0xF0) | ((minute % 10) & 0x0F));
@@ -314,7 +314,7 @@
 
         public long toMillis() {
             LocalDateTime localDateTime =
-                    LocalDateTime.of(year, month + 1, monthDay, hour, minute, second);
+                    LocalDateTime.of(year, monthOrdinal, monthDay, hour, minute, second);
             Instant instant = localDateTime.toInstant(mZoneId.getRules().getOffset(localDateTime));
             return instant.toEpochMilli();
         }
@@ -325,7 +325,7 @@
             StringBuilder builder = new StringBuilder();
             builder.append("TimeStamp ");
             builder.append("{ year=" + year);
-            builder.append(", month=" + month);
+            builder.append(", month=" + monthOrdinal);
             builder.append(", day=" + monthDay);
             builder.append(", hour=" + hour);
             builder.append(", minute=" + minute);
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 616b6b0..248c117 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -28,7 +28,7 @@
         ":framework_native_aidl",
     ],
     libs: [
-        "framework-all",
+        "framework",
         "app-compat-annotations",
         "unsupportedappusage",
     ],
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index d1da47f..a331ec5 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -9,6 +9,14 @@
         <option name="screen-always-on" value="on" />
         <!-- prevents the phone from restarting -->
         <option name="force-skip-system-props" value="true" />
+        <!-- set WM tracing verbose level to all -->
+        <option name="run-command" value="adb shell cmd window tracing level all" />
+        <!-- inform WM to log all transactions -->
+        <option name="run-command" value="adb shell cmd window tracing transaction" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
+        <!-- keeps the screen on during tests -->
+        <option name="cleanup-action" value="REBOOT" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true"/>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
index 5a66e80..ad64840 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
@@ -144,6 +144,19 @@
         );
     }
 
+    @Test
+    public void checkVisibility_screenshotLayerBecomesInvisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(mTestApp.getPackage())
+                .then()
+                .replaceVisibleLayer(mTestApp.getPackage(), "Screenshot")
+                .then()
+                .showsLayer(mTestApp.getPackage()).and().showsLayer("Screenshot")
+                .then()
+                .replaceVisibleLayer("Screenshot", mTestApp.getPackage())
+                .forAllEntries());
+    }
+
     @FlakyTest(bugId = 140855415)
     @Ignore("Waiting bug feedback")
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
index e033d0a..0201a95 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
@@ -20,7 +20,6 @@
 import static android.view.Surface.rotationToString;
 
 import static com.android.server.wm.flicker.helpers.AutomationUtils.clearRecents;
-import static com.android.server.wm.flicker.helpers.AutomationUtils.closePipWindow;
 import static com.android.server.wm.flicker.helpers.AutomationUtils.exitSplitScreen;
 import static com.android.server.wm.flicker.helpers.AutomationUtils.expandPipWindow;
 import static com.android.server.wm.flicker.helpers.AutomationUtils.launchSplitScreen;
@@ -176,11 +175,15 @@
                 .repeat(ITERATIONS);
     }
 
-    static TransitionBuilder appToSplitScreen(IAppHelper testApp, UiDevice device) {
+    static TransitionBuilder appToSplitScreen(IAppHelper testApp, UiDevice device,
+            int beginRotation) {
+        final String testTag = "appToSplitScreen_" + testApp.getLauncherName() + "_"
+                + rotationToString(beginRotation);
         return TransitionRunner.newBuilder()
-                .withTag("appToSplitScreen_" + testApp.getLauncherName())
+                .withTag(testTag)
                 .recordAllRuns()
                 .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBeforeAll(() -> setRotation(device, beginRotation))
                 .runBefore(testApp::open)
                 .runBefore(device::waitForIdle)
                 .runBefore(() -> sleep(500))
@@ -285,41 +288,52 @@
                 .repeat(ITERATIONS);
     }
 
-    static TransitionBuilder enterPipMode(PipAppHelper testApp, UiDevice device) {
+    static TransitionBuilder enterPipMode(PipAppHelper testApp, UiDevice device,
+            int beginRotation) {
         return TransitionRunner.newBuilder()
-                .withTag("enterPipMode_" + testApp.getLauncherName())
+                .withTag("enterPipMode_" + testApp.getLauncherName()
+                        + rotationToString(beginRotation))
                 .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
                 .runBefore(device::pressHome)
+                .runBefore(() -> setRotation(device, beginRotation))
                 .runBefore(testApp::open)
                 .run(() -> testApp.clickEnterPipButton(device))
-                .runAfter(() -> closePipWindow(device))
+                .runAfter(() -> testApp.closePipWindow(device))
                 .runAfterAll(testApp::exit)
                 .repeat(ITERATIONS);
     }
 
-    static TransitionBuilder exitPipModeToHome(PipAppHelper testApp, UiDevice device) {
+    static TransitionBuilder exitPipModeToHome(PipAppHelper testApp, UiDevice device,
+            int beginRotation) {
         return TransitionRunner.newBuilder()
-                .withTag("exitPipModeToHome_" + testApp.getLauncherName())
+                .withTag("exitPipModeToHome_" + testApp.getLauncherName()
+                        + rotationToString(beginRotation))
+                .recordAllRuns()
                 .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
                 .runBefore(device::pressHome)
+                .runBefore(() -> setRotation(device, beginRotation))
                 .runBefore(testApp::open)
-                .runBefore(() -> testApp.clickEnterPipButton(device))
-                .run(() -> closePipWindow(device))
+                .run(() -> testApp.clickEnterPipButton(device))
+                .run(() -> testApp.closePipWindow(device))
                 .run(device::waitForIdle)
-                .runAfterAll(testApp::exit)
+                .run(testApp::exit)
                 .repeat(ITERATIONS);
     }
 
-    static TransitionBuilder exitPipModeToApp(PipAppHelper testApp, UiDevice device) {
+    static TransitionBuilder exitPipModeToApp(PipAppHelper testApp, UiDevice device,
+            int beginRotation) {
         return TransitionRunner.newBuilder()
-                .withTag("exitPipModeToApp_" + testApp.getLauncherName())
+                .withTag("exitPipModeToApp_" + testApp.getLauncherName()
+                        + rotationToString(beginRotation))
+                .recordAllRuns()
                 .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
-                .runBefore(device::pressHome)
-                .runBefore(testApp::open)
-                .runBefore(() -> testApp.clickEnterPipButton(device))
+                .run(device::pressHome)
+                .run(() -> setRotation(device, beginRotation))
+                .run(testApp::open)
+                .run(() -> testApp.clickEnterPipButton(device))
                 .run(() -> expandPipWindow(device))
                 .run(device::waitForIdle)
-                .runAfterAll(testApp::exit)
+                .run(testApp::exit)
                 .repeat(ITERATIONS);
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
index 8f0177c..666a0b9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
@@ -94,7 +94,8 @@
      */
     @Test
     public void openAppToSplitScreen() {
-        CommonTransitions.appToSplitScreen(testApp, uiDevice).includeJankyRuns().recordAllRuns()
+        CommonTransitions.appToSplitScreen(testApp, uiDevice,
+                Surface.ROTATION_0).includeJankyRuns().recordAllRuns()
                 .build().run();
     }
 
@@ -116,7 +117,7 @@
         ImeAppHelper bottomApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
         CommonTransitions.resizeSplitScreen(testApp, bottomApp, uiDevice, Surface.ROTATION_0,
                 new Rational(1, 3), new Rational(2, 3))
-                .includeJankyRuns().recordEachRun().build().run();
+                .includeJankyRuns().build().run();
     }
 
     // IME tests
@@ -128,7 +129,7 @@
     public void editTextSetFocus() {
         ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
         CommonTransitions.editTextSetFocus(testApp, uiDevice, Surface.ROTATION_0)
-                .includeJankyRuns().recordEachRun()
+                .includeJankyRuns()
                 .build().run();
     }
 
@@ -139,7 +140,7 @@
     public void editTextLoseFocusToHome() {
         ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
         CommonTransitions.editTextLoseFocusToHome(testApp, uiDevice, Surface.ROTATION_0)
-                .includeJankyRuns().recordEachRun()
+                .includeJankyRuns()
                 .build().run();
     }
 
@@ -150,7 +151,7 @@
     public void editTextLoseFocusToApp() {
         ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
         CommonTransitions.editTextLoseFocusToHome(testApp, uiDevice, Surface.ROTATION_0)
-                .includeJankyRuns().recordEachRun()
+                .includeJankyRuns()
                 .build().run();
     }
 
@@ -162,7 +163,7 @@
     @Test
     public void enterPipMode() {
         PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
-        CommonTransitions.enterPipMode(testApp, uiDevice).includeJankyRuns().recordEachRun()
+        CommonTransitions.enterPipMode(testApp, uiDevice, Surface.ROTATION_0).includeJankyRuns()
                 .build().run();
     }
 
@@ -172,7 +173,8 @@
     @Test
     public void exitPipModeToHome() {
         PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
-        CommonTransitions.exitPipModeToHome(testApp, uiDevice).includeJankyRuns().recordEachRun()
+        CommonTransitions.exitPipModeToHome(testApp, uiDevice, Surface.ROTATION_0)
+                .includeJankyRuns()
                 .build().run();
     }
 
@@ -182,7 +184,7 @@
     @Test
     public void exitPipModeToApp() {
         PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
-        CommonTransitions.exitPipModeToApp(testApp, uiDevice).includeJankyRuns().recordEachRun()
+        CommonTransitions.exitPipModeToApp(testApp, uiDevice, Surface.ROTATION_0).includeJankyRuns()
                 .build().run();
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
index 883d59e..4578fa3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
@@ -89,7 +89,7 @@
                         }
                         if (result.screenCaptureVideoExists()) {
                             Log.e(TAG, "Screen capture video saved to " + result
-                                    .screenCaptureVideo.toString());
+                                    .screenCaptureVideoPath().toString());
                         }
                     }
                 });
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
index efdfaee..2981ff9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
@@ -19,6 +19,8 @@
 import static com.android.server.wm.flicker.CommonTransitions.openAppCold;
 import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
 
+import android.view.Surface;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
@@ -76,10 +78,20 @@
 
     @Test
     public void checkVisibility_wallpaperLayerBecomesInvisible() {
-        checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer("Wallpaper")
-                .then()
-                .hidesLayer("Wallpaper")
-                .forAllEntries());
+        if (mBeginRotation == Surface.ROTATION_0) {
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .showsLayer("Wallpaper")
+                    .then()
+                    .replaceVisibleLayer("Wallpaper", mTestApp.getPackage())
+                    .forAllEntries());
+        } else {
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .showsLayer("Wallpaper")
+                    .then()
+                    .replaceVisibleLayer("Wallpaper", "Screenshot")
+                    .then()
+                    .showsLayer(mTestApp.getPackage())
+                    .forAllEntries());
+        }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
index f8b7938..ddead6d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
@@ -17,35 +17,38 @@
 package com.android.server.wm.flicker;
 
 import static com.android.server.wm.flicker.CommonTransitions.appToSplitScreen;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
 
 /**
  * Test open app to split screen.
  * To run this test: {@code atest FlickerTests:OpenAppToSplitScreenTest}
  */
 @LargeTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class OpenAppToSplitScreenTest extends FlickerTestBase {
+public class OpenAppToSplitScreenTest extends NonRotationTestBase {
 
-    public OpenAppToSplitScreenTest() {
+    public OpenAppToSplitScreenTest(String beginRotationName, int beginRotation) {
+        super(beginRotationName, beginRotation);
+
         this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
                 "com.android.server.wm.flicker.testapp", "SimpleApp");
     }
 
     @Before
     public void runTransition() {
-        super.runTransition(appToSplitScreen(mTestApp, mUiDevice).includeJankyRuns().build());
+        super.runTransition(appToSplitScreen(mTestApp, mUiDevice, mBeginRotation)
+                .includeJankyRuns()
+                .build());
     }
 
     @Test
@@ -70,25 +73,6 @@
     }
 
     @Test
-    public void checkCoveredRegion_noUncoveredRegions() {
-        checkResults(result ->
-                LayersTraceSubject.assertThat(result)
-                        .coversRegion(getDisplayBounds()).forAllEntries());
-    }
-
-    @Test
-    public void checkVisibility_navBarLayerIsAlwaysVisible() {
-        checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
-    }
-
-    @Test
-    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
-        checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
-    }
-
-    @Test
     public void checkVisibility_dividerLayerBecomesVisible() {
         checkResults(result -> LayersTraceSubject.assertThat(result)
                 .hidesLayer(DOCKED_STACK_DIVIDER)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
index 7ce6315..bb684d1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
@@ -19,6 +19,8 @@
 import static com.android.server.wm.flicker.CommonTransitions.openAppWarm;
 import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
 
+import android.view.Surface;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
@@ -76,10 +78,20 @@
 
     @Test
     public void checkVisibility_wallpaperLayerBecomesInvisible() {
-        checkResults(result -> LayersTraceSubject.assertThat(result)
-                .showsLayer("Wallpaper")
-                .then()
-                .hidesLayer("Wallpaper")
-                .forAllEntries());
+        if (mBeginRotation == Surface.ROTATION_0) {
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .showsLayer("Wallpaper")
+                    .then()
+                    .replaceVisibleLayer("Wallpaper", mTestApp.getPackage())
+                    .forAllEntries());
+        } else {
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .showsLayer("Wallpaper")
+                    .then()
+                    .replaceVisibleLayer("Wallpaper", "Screenshot")
+                    .then()
+                    .showsLayer(mTestApp.getPackage())
+                    .forAllEntries());
+        }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/PipToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/PipToAppTest.java
new file mode 100644
index 0000000..85706bd
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/PipToAppTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.exitPipModeToApp;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
+import com.android.server.wm.flicker.helpers.PipAppHelper;
+
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+
+/**
+ * Test Pip launch.
+ * To run this test: {@code atest FlickerTests:PipToAppTest}
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class PipToAppTest extends NonRotationTestBase {
+
+    static final String sPipWindowTitle = "PipMenuActivity";
+
+    public PipToAppTest(String beginRotationName, int beginRotation) {
+        super(beginRotationName, beginRotation);
+
+        this.mTestApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
+    }
+
+    @Before
+    public void runTransition() {
+        run(exitPipModeToApp((PipAppHelper) mTestApp, mUiDevice, mBeginRotation)
+                .includeJankyRuns().build());
+    }
+
+    @Test
+    public void checkVisibility_pipWindowBecomesVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .skipUntilFirstAssertion()
+                .showsAppWindowOnTop(sPipWindowTitle)
+                .then()
+                .hidesAppWindow(sPipWindowTitle)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_pipLayerBecomesVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .skipUntilFirstAssertion()
+                .showsLayer(sPipWindowTitle)
+                .then()
+                .hidesLayer(sPipWindowTitle)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_backgroundWindowVisibleBehindPipLayer() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .skipUntilFirstAssertion()
+                .showsAppWindowOnTop(sPipWindowTitle)
+                .then()
+                .showsBelowAppWindow("Wallpaper")
+                .then()
+                .showsAppWindowOnTop(mTestApp.getPackage())
+                .then()
+                .hidesAppWindowOnTop(mTestApp.getPackage())
+                .forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/PipToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/PipToHomeTest.java
new file mode 100644
index 0000000..ef856dc
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/PipToHomeTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.exitPipModeToHome;
+
+import android.view.Surface;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.wm.flicker.helpers.PipAppHelper;
+
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+
+/**
+ * Test Pip launch.
+ * To run this test: {@code atest FlickerTests:PipToHomeTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class PipToHomeTest extends FlickerTestBase {
+
+    static final String sPipWindowTitle = "PipActivity";
+
+    // public PipToHomeTest(String beginRotationName, int beginRotation) {
+    public PipToHomeTest() {
+        // super(beginRotationName, beginRotation);
+
+        this.mTestApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
+    }
+
+    @Before
+    public void runTransition() {
+        // run(exitPipModeToHome((PipAppHelper) mTestApp, mUiDevice, mBeginRotation)
+        run(exitPipModeToHome((PipAppHelper) mTestApp, mUiDevice, Surface.ROTATION_0)
+                .includeJankyRuns().build());
+    }
+
+    @Ignore
+    @Test
+    public void checkVisibility_pipWindowBecomesVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .skipUntilFirstAssertion()
+                .showsAppWindowOnTop(sPipWindowTitle)
+                .then()
+                .hidesAppWindow(sPipWindowTitle)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_pipLayerBecomesVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .skipUntilFirstAssertion()
+                .showsLayer(sPipWindowTitle)
+                .then()
+                .hidesLayer(sPipWindowTitle)
+                .forAllEntries());
+    }
+
+    @Ignore
+    @Test
+    public void checkVisibility_backgroundWindowVisibleBehindPipLayer() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAppWindowOnTop(sPipWindowTitle)
+                .then()
+                .showsBelowAppWindow("Wallpaper")
+                .then()
+                .showsAppWindowOnTop("Wallpaper")
+                .forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
index 29b6240..e36701b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
@@ -95,7 +95,7 @@
         Rect displayBounds = getDisplayBounds();
         checkResults(result -> {
             LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
-                    result.getLayersTracePath());
+                    result.getLayersTracePath(), result.getLayersTraceChecksum());
 
             assertThat(entries.getEntries()).isNotEmpty();
             Rect startingDividerBounds = entries.getEntries().get(0).getVisibleBounds
@@ -124,7 +124,7 @@
         Rect displayBounds = getDisplayBounds();
         checkResults(result -> {
             LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
-                    result.getLayersTracePath());
+                    result.getLayersTracePath(), result.getLayersTraceChecksum());
 
             assertThat(entries.getEntries()).isNotEmpty();
             Rect endingDividerBounds = entries.getEntries().get(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java
index d00e11b..d5f9a20 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java
@@ -40,4 +40,8 @@
         }
     }
 
+    public void closePipWindow(UiDevice device) {
+        AutomationUtils.closePipWindow(device);
+    }
+
 }
diff --git a/tests/RcsTests/Android.bp b/tests/RcsTests/Android.bp
deleted file mode 100644
index 8ee4960..0000000
--- a/tests/RcsTests/Android.bp
+++ /dev/null
@@ -1,17 +0,0 @@
-android_test {
-    name: "RcsTests",
-    // Only compile source java files in this apk.
-    srcs: ["src/**/*.java"],
-    platform_apis: true,
-    certificate: "platform",
-    libs: [
-        "android.test.runner",
-        "android.test.base",
-    ],
-    static_libs: [
-        "junit",
-        "androidx.test.rules",
-        "mockito-target-minus-junit4",
-        "truth-prebuilt",
-    ],
-}
diff --git a/tests/RcsTests/AndroidManifest.xml b/tests/RcsTests/AndroidManifest.xml
deleted file mode 100644
index b1706a0..0000000
--- a/tests/RcsTests/AndroidManifest.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.tests.rcs">
-    <application android:label="RCS Test">
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.tests.rcs"/>
-</manifest>
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java
deleted file mode 100644
index 6c311f9..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.telephony.ims.RcsGroupThreadIconChangedEvent;
-import android.telephony.ims.RcsGroupThreadIconChangedEventDescriptor;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsGroupThreadIconChangedEventTest {
-
-    @Test
-    public void testCanUnparcel() {
-        int rcsGroupThreadId = 1;
-        int rcsParticipantId = 2;
-        Uri newIconUri = Uri.parse("content://new_icon");
-
-        RcsGroupThreadIconChangedEventDescriptor iconChangedEventDescriptor =
-                new RcsGroupThreadIconChangedEventDescriptor(1234567890, rcsGroupThreadId,
-                        rcsParticipantId, newIconUri);
-
-        Parcel parcel = Parcel.obtain();
-        iconChangedEventDescriptor.writeToParcel(
-                parcel, iconChangedEventDescriptor.describeContents());
-
-        parcel.setDataPosition(0);
-
-        iconChangedEventDescriptor =
-                RcsGroupThreadIconChangedEventDescriptor.CREATOR.createFromParcel(parcel);
-
-        RcsGroupThreadIconChangedEvent iconChangedEvent =
-                iconChangedEventDescriptor.createRcsEvent(null);
-
-        assertThat(iconChangedEvent.getNewIcon()).isEqualTo(newIconUri);
-        assertThat(iconChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
-        assertThat(iconChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
-        assertThat(iconChangedEvent.getTimestamp()).isEqualTo(1234567890);
-    }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java
deleted file mode 100644
index a60dabb..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.telephony.ims.RcsGroupThreadNameChangedEvent;
-import android.telephony.ims.RcsGroupThreadNameChangedEventDescriptor;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsGroupThreadNameChangedEventTest {
-    @Test
-    public void testCanUnparcel() {
-        String newName = "new name";
-
-        int rcsGroupThreadId = 1;
-        int rcsParticipantId = 2;
-
-        RcsGroupThreadNameChangedEventDescriptor nameChangedEventDescriptor =
-                new RcsGroupThreadNameChangedEventDescriptor(
-                        1234567890, rcsGroupThreadId, rcsParticipantId, newName);
-
-        Parcel parcel = Parcel.obtain();
-        nameChangedEventDescriptor.writeToParcel(
-                parcel, nameChangedEventDescriptor.describeContents());
-
-        parcel.setDataPosition(0);
-
-        nameChangedEventDescriptor = RcsGroupThreadNameChangedEventDescriptor.CREATOR
-                .createFromParcel(parcel);
-
-        RcsGroupThreadNameChangedEvent nameChangedEvent =
-                nameChangedEventDescriptor.createRcsEvent(null);
-
-        assertThat(nameChangedEvent.getNewName()).isEqualTo(newName);
-        assertThat(nameChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
-        assertThat(nameChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
-        assertThat(nameChangedEvent.getTimestamp()).isEqualTo(1234567890);
-    }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java
deleted file mode 100644
index 7b02cb1..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.telephony.ims.RcsGroupThreadParticipantJoinedEvent;
-import android.telephony.ims.RcsGroupThreadParticipantJoinedEventDescriptor;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsGroupThreadParticipantJoinedEventTest {
-
-    @Test
-    public void testCanUnparcel() {
-        int rcsGroupThreadId = 1;
-        int rcsParticipantId = 2;
-
-        RcsGroupThreadParticipantJoinedEventDescriptor participantJoinedEventDescriptor =
-                new RcsGroupThreadParticipantJoinedEventDescriptor(
-                        1234567890, rcsGroupThreadId, rcsParticipantId, rcsParticipantId);
-
-        Parcel parcel = Parcel.obtain();
-        participantJoinedEventDescriptor.writeToParcel(
-                parcel, participantJoinedEventDescriptor.describeContents());
-
-        parcel.setDataPosition(0);
-
-        participantJoinedEventDescriptor = RcsGroupThreadParticipantJoinedEventDescriptor.CREATOR
-                .createFromParcel(parcel);
-
-        RcsGroupThreadParticipantJoinedEvent participantJoinedEvent =
-                participantJoinedEventDescriptor.createRcsEvent(null);
-
-        assertThat(participantJoinedEvent.getJoinedParticipant().getId()).isEqualTo(2);
-        assertThat(participantJoinedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
-        assertThat(participantJoinedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
-        assertThat(participantJoinedEvent.getTimestamp()).isEqualTo(1234567890);
-    }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java
deleted file mode 100644
index 51466bd..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.telephony.ims.RcsGroupThreadParticipantLeftEvent;
-import android.telephony.ims.RcsGroupThreadParticipantLeftEventDescriptor;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsGroupThreadParticipantLeftEventTest {
-    @Test
-    public void testCanUnparcel() {
-        int rcsGroupThreadId = 1;
-        int rcsParticipantId = 2;
-
-        RcsGroupThreadParticipantLeftEventDescriptor participantLeftEventDescriptor =
-                new RcsGroupThreadParticipantLeftEventDescriptor(
-                        1234567890, rcsGroupThreadId, rcsParticipantId, rcsParticipantId);
-
-        Parcel parcel = Parcel.obtain();
-        participantLeftEventDescriptor.writeToParcel(
-                parcel, participantLeftEventDescriptor.describeContents());
-
-        parcel.setDataPosition(0);
-
-        // create from parcel
-        parcel.setDataPosition(0);
-        participantLeftEventDescriptor = RcsGroupThreadParticipantLeftEventDescriptor.CREATOR
-                .createFromParcel(parcel);
-
-        RcsGroupThreadParticipantLeftEvent participantLeftEvent =
-                participantLeftEventDescriptor.createRcsEvent(null);
-
-        assertThat(participantLeftEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
-        assertThat(participantLeftEvent.getLeavingParticipant().getId()).isEqualTo(2);
-        assertThat(participantLeftEvent.getTimestamp()).isEqualTo(1234567890);
-    }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java
deleted file mode 100644
index 56830df..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.telephony.ims.RcsParticipantAliasChangedEvent;
-import android.telephony.ims.RcsParticipantAliasChangedEventDescriptor;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsParticipantAliasChangedEventTest {
-    private static final String OLD_ALIAS = "old alias";
-    private static final String NEW_ALIAS = "new alias";
-    private int mParticipantId = 3;
-
-    @Test
-    public void testCanUnparcel() {
-        RcsParticipantAliasChangedEventDescriptor aliasChangedEventDescriptor =
-                new RcsParticipantAliasChangedEventDescriptor(
-                        1234567890, mParticipantId, NEW_ALIAS);
-
-        Parcel parcel = Parcel.obtain();
-        aliasChangedEventDescriptor.writeToParcel(
-                parcel, aliasChangedEventDescriptor.describeContents());
-
-        parcel.setDataPosition(0);
-
-        aliasChangedEventDescriptor = RcsParticipantAliasChangedEventDescriptor.CREATOR
-                .createFromParcel(parcel);
-
-        RcsParticipantAliasChangedEvent aliasChangedEvent =
-                aliasChangedEventDescriptor.createRcsEvent(null);
-
-        assertThat(aliasChangedEvent.getParticipant().getId()).isEqualTo(mParticipantId);
-        assertThat(aliasChangedEvent.getNewAlias()).isEqualTo(NEW_ALIAS);
-        assertThat(aliasChangedEvent.getTimestamp()).isEqualTo(1234567890);
-    }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java
deleted file mode 100644
index 2d95513b..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.telephony.ims.RcsParticipantQueryParams;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsParticipantQueryParamsTest {
-
-    @Test
-    public void testCanUnparcel() {
-        RcsParticipantQueryParams rcsParticipantQueryParams =
-                new RcsParticipantQueryParams.Builder()
-                        .setAliasLike("%alias_")
-                        .setCanonicalAddressLike("_canonical%")
-                        .setSortProperty(RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS)
-                        .setSortDirection(true)
-                        .setResultLimit(432)
-                        .build();
-
-
-        Parcel parcel = Parcel.obtain();
-        rcsParticipantQueryParams.writeToParcel(parcel,
-                rcsParticipantQueryParams.describeContents());
-
-        parcel.setDataPosition(0);
-        rcsParticipantQueryParams = RcsParticipantQueryParams.CREATOR.createFromParcel(
-                parcel);
-
-        assertThat(rcsParticipantQueryParams.getAliasLike()).isEqualTo("%alias_");
-        assertThat(rcsParticipantQueryParams.getCanonicalAddressLike()).contains("_canonical%");
-        assertThat(rcsParticipantQueryParams.getLimit()).isEqualTo(432);
-        assertThat(rcsParticipantQueryParams.getSortingProperty()).isEqualTo(
-                RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS);
-        assertThat(rcsParticipantQueryParams.getSortDirection()).isTrue();
-    }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java
deleted file mode 100644
index 7a3e384..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.tests.ims;
-
-import static android.telephony.ims.RcsThreadQueryParams.SORT_BY_TIMESTAMP;
-import static android.telephony.ims.RcsThreadQueryParams.THREAD_TYPE_GROUP;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.telephony.ims.RcsParticipant;
-import android.telephony.ims.RcsThreadQueryParams;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsThreadQueryParamsTest {
-
-    @Test
-    public void testCanUnparcel() {
-        RcsParticipant rcsParticipant = new RcsParticipant(null, 1);
-        RcsThreadQueryParams rcsThreadQueryParams = new RcsThreadQueryParams.Builder()
-                .setThreadType(THREAD_TYPE_GROUP)
-                .setParticipant(rcsParticipant)
-                .setResultLimit(50)
-                .setSortProperty(SORT_BY_TIMESTAMP)
-                .setSortDirection(true)
-                .build();
-
-        Parcel parcel = Parcel.obtain();
-        rcsThreadQueryParams.writeToParcel(parcel, rcsThreadQueryParams.describeContents());
-
-        parcel.setDataPosition(0);
-        rcsThreadQueryParams = RcsThreadQueryParams.CREATOR.createFromParcel(parcel);
-
-        assertThat(rcsThreadQueryParams.getThreadType()).isEqualTo(THREAD_TYPE_GROUP);
-        assertThat(rcsThreadQueryParams.getRcsParticipantsIds())
-                .contains(rcsParticipant.getId());
-        assertThat(rcsThreadQueryParams.getLimit()).isEqualTo(50);
-        assertThat(rcsThreadQueryParams.getSortingProperty()).isEqualTo(SORT_BY_TIMESTAMP);
-        assertThat(rcsThreadQueryParams.getSortDirection()).isTrue();
-    }
-}
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
index 638b6d1..480b12b 100644
--- a/tests/net/AndroidManifest.xml
+++ b/tests/net/AndroidManifest.xml
@@ -50,6 +50,7 @@
 
     <application>
         <uses-library android:name="android.test.runner" />
+        <uses-library android:name="android.net.ipsec.ike" />
     </application>
 
     <instrumentation
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index f25fd4d..48b65e5 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -315,7 +315,7 @@
         source.addDnsServer(DNS1);
         source.addDnsServer(DNS2);
         // set 2 gateways
-        source.addRoute(new RouteInfo(GATEWAY1));
+        source.addRoute(new RouteInfo(LINKADDRV4, GATEWAY1));
         source.addRoute(new RouteInfo(GATEWAY2));
         source.setMtu(MTU);
 
@@ -327,7 +327,7 @@
         target.addDnsServer(DNS2);
         target.addDnsServer(DNS1);
         target.addRoute(new RouteInfo(GATEWAY2));
-        target.addRoute(new RouteInfo(GATEWAY1));
+        target.addRoute(new RouteInfo(LINKADDRV4, GATEWAY1));
         target.setMtu(MTU);
 
         assertLinkPropertiesEqual(source, target);
@@ -364,12 +364,13 @@
 
     @Test
     public void testRouteInterfaces() {
-        LinkAddress prefix = new LinkAddress(address("2001:db8::"), 32);
+        LinkAddress prefix1 = new LinkAddress(address("2001:db8:1::"), 48);
+        LinkAddress prefix2 = new LinkAddress(address("2001:db8:2::"), 48);
         InetAddress address = ADDRV6;
 
         // Add a route with no interface to a LinkProperties with no interface. No errors.
         LinkProperties lp = new LinkProperties();
-        RouteInfo r = new RouteInfo(prefix, address, null);
+        RouteInfo r = new RouteInfo(prefix1, address, null);
         assertTrue(lp.addRoute(r));
         assertEquals(1, lp.getRoutes().size());
         assertAllRoutesHaveInterface(null, lp);
@@ -379,7 +380,7 @@
         assertEquals(1, lp.getRoutes().size());
 
         // Add a route with an interface. Expect an exception.
-        r = new RouteInfo(prefix, address, "wlan0");
+        r = new RouteInfo(prefix2, address, "wlan0");
         try {
           lp.addRoute(r);
           fail("Adding wlan0 route to LP with no interface, expect exception");
@@ -398,7 +399,7 @@
         } catch (IllegalArgumentException expected) {}
 
         // If the interface name matches, the route is added.
-        r = new RouteInfo(prefix, null, "wlan0");
+        r = new RouteInfo(prefix2, null, "wlan0");
         lp.setInterfaceName("wlan0");
         lp.addRoute(r);
         assertEquals(2, lp.getRoutes().size());
@@ -423,10 +424,12 @@
         assertEquals(3, lp.compareAllRoutes(lp2).added.size());
         assertEquals(3, lp.compareAllRoutes(lp2).removed.size());
 
-        // Check remove works
-        lp.removeRoute(new RouteInfo(prefix, address, null));
+        // Remove route with incorrect interface, no route removed.
+        lp.removeRoute(new RouteInfo(prefix2, null, null));
         assertEquals(3, lp.getRoutes().size());
-        lp.removeRoute(new RouteInfo(prefix, address, "wlan0"));
+
+        // Check remove works when interface is correct.
+        lp.removeRoute(new RouteInfo(prefix2, null, "wlan0"));
         assertEquals(2, lp.getRoutes().size());
         assertAllRoutesHaveInterface("wlan0", lp);
         assertAllRoutesNotHaveInterface("p2p0", lp);
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
index 490c4679..23caf49 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
@@ -26,6 +26,7 @@
 import android.os.IBinder
 import com.android.networkstack.metrics.DataStallStatsUtils
 import com.android.networkstack.netlink.TcpSocketTracker
+import com.android.server.NetworkStackService
 import com.android.server.NetworkStackService.NetworkMonitorConnector
 import com.android.server.NetworkStackService.NetworkStackConnector
 import com.android.server.connectivity.NetworkMonitor
@@ -88,6 +89,7 @@
             val nm = NetworkMonitor(this@TestNetworkStackService, cb,
                     this.network,
                     mock(IpConnectivityLog::class.java), mock(SharedLog::class.java),
+                    mock(NetworkStackService.NetworkStackServiceManager::class.java),
                     NetworkMonitorDeps(privateDnsBypassNetwork),
                     mock(DataStallStatsUtils::class.java),
                     mock(TcpSocketTracker::class.java))
diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java
index d6a2176..2273bc6 100644
--- a/tests/net/java/android/net/Ikev2VpnProfileTest.java
+++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java
@@ -22,7 +22,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
 
 import android.test.mock.MockContext;
 
@@ -232,10 +231,12 @@
         builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
         final VpnProfile profile = builder.build().toVpnProfile();
 
+        final String expectedSecret = Ikev2VpnProfile.PREFIX_INLINE
+                + Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded());
         verifyVpnProfileCommon(profile);
         assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert);
         assertEquals(
-                Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded()),
+                expectedSecret,
                 profile.ipsecSecret);
         assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
 
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 6fb4612..141e68a 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -78,6 +78,7 @@
 import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 import static android.net.RouteInfo.RTN_UNREACHABLE;
+import static android.system.OsConstants.IPPROTO_TCP;
 
 import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType;
 import static com.android.testutils.ConcurrentUtilsKt.await;
@@ -138,6 +139,7 @@
 import android.content.res.Resources;
 import android.location.LocationManager;
 import android.net.CaptivePortalData;
+import android.net.ConnectionInfo;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.ConnectivityManager.PacketKeepalive;
@@ -153,6 +155,7 @@
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
+import android.net.InetAddresses;
 import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
 import android.net.IpSecManager;
@@ -176,6 +179,7 @@
 import android.net.SocketKeepalive;
 import android.net.UidRange;
 import android.net.Uri;
+import android.net.VpnManager;
 import android.net.metrics.IpConnectivityLog;
 import android.net.shared.NetworkMonitorUtils;
 import android.net.shared.PrivateDnsConfig;
@@ -200,6 +204,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.security.KeyStore;
 import android.system.Os;
 import android.test.mock.MockContentResolver;
 import android.text.TextUtils;
@@ -272,6 +277,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Predicate;
+import java.util.function.Supplier;
 
 import kotlin.reflect.KClass;
 
@@ -445,15 +451,21 @@
             return mPackageManager;
         }
 
+        private int checkMockedPermission(String permission, Supplier<Integer> ifAbsent) {
+            final Integer granted = mMockedPermissions.get(permission);
+            return granted != null ? granted : ifAbsent.get();
+        }
+
         @Override
         public int checkPermission(String permission, int pid, int uid) {
-            final Integer granted = mMockedPermissions.get(permission);
-            if (granted == null) {
-                // All non-mocked permissions should be held by the test or unnecessary: check as
-                // normal to make sure the code does not rely on unexpected permissions.
-                return super.checkPermission(permission, pid, uid);
-            }
-            return granted;
+            return checkMockedPermission(
+                    permission, () -> super.checkPermission(permission, pid, uid));
+        }
+
+        @Override
+        public int checkCallingOrSelfPermission(String permission) {
+            return checkMockedPermission(
+                    permission, () -> super.checkCallingOrSelfPermission(permission));
         }
 
         @Override
@@ -1002,12 +1014,13 @@
         // Careful ! This is different from mNetworkAgent, because MockNetworkAgent does
         // not inherit from NetworkAgent.
         private TestNetworkAgentWrapper mMockNetworkAgent;
+        private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
 
         private VpnInfo mVpnInfo;
 
         public MockVpn(int userId) {
             super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
-                    userId);
+                    userId, mock(KeyStore.class));
         }
 
         public void setNetworkAgent(TestNetworkAgentWrapper agent) {
@@ -1022,6 +1035,10 @@
             updateCapabilities(null /* defaultNetwork */);
         }
 
+        public void setVpnType(int vpnType) {
+            mVpnType = vpnType;
+        }
+
         @Override
         public int getNetId() {
             if (mMockNetworkAgent == null) {
@@ -1040,6 +1057,11 @@
             return mConnected;  // Similar trickery
         }
 
+        @Override
+        public int getActiveAppVpnType() {
+            return mVpnType;
+        }
+
         private void connect(boolean isAlwaysMetered) {
             mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
             mConnected = true;
@@ -1158,6 +1180,10 @@
                 Arrays.asList(new UserInfo[] {
                         new UserInfo(VPN_USER, "", 0),
                 }));
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
+        when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
+                .thenReturn(applicationInfo);
 
         // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
         // http://b/25897652 .
@@ -3019,7 +3045,7 @@
             networkCapabilities.addTransportType(TRANSPORT_WIFI)
                     .setNetworkSpecifier(new MatchAllNetworkSpecifier());
             mService.requestNetwork(networkCapabilities, null, 0, null,
-                    ConnectivityManager.TYPE_WIFI, TEST_PACKAGE_NAME);
+                    ConnectivityManager.TYPE_WIFI, mContext.getPackageName());
         });
 
         class NonParcelableSpecifier extends NetworkSpecifier {
@@ -5901,6 +5927,12 @@
         final LinkAddress myIpv6 = new LinkAddress("2001:db8:1::1/64");
         final String kNat64PrefixString = "2001:db8:64:64:64:64::";
         final IpPrefix kNat64Prefix = new IpPrefix(InetAddress.getByName(kNat64PrefixString), 96);
+        final RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, myIpv6.getAddress(),
+                                                     MOBILE_IFNAME);
+        final RouteInfo hostRoute = new RouteInfo(myIpv6, null, MOBILE_IFNAME);
+        final RouteInfo ipv4Default = new RouteInfo(myIpv4, null, MOBILE_IFNAME);
+        final RouteInfo stackedDefault = new RouteInfo((IpPrefix) null, myIpv4.getAddress(),
+                                                       CLAT_PREFIX + MOBILE_IFNAME);
 
         final NetworkRequest networkRequest = new NetworkRequest.Builder()
                 .addTransportType(TRANSPORT_CELLULAR)
@@ -5913,15 +5945,13 @@
         final LinkProperties cellLp = new LinkProperties();
         cellLp.setInterfaceName(MOBILE_IFNAME);
         cellLp.addLinkAddress(myIpv6);
-        cellLp.addRoute(new RouteInfo((IpPrefix) null, myIpv6.getAddress(), MOBILE_IFNAME));
-        cellLp.addRoute(new RouteInfo(myIpv6, null, MOBILE_IFNAME));
+        cellLp.addRoute(defaultRoute);
+        cellLp.addRoute(hostRoute);
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
         reset(mNetworkManagementService);
         reset(mMockDnsResolver);
         reset(mMockNetd);
         reset(mBatteryStatsService);
-        when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
-                .thenReturn(getClatInterfaceConfig(myIpv4));
 
         // Connect with ipv6 link properties. Expect prefix discovery to be started.
         mCellNetworkAgent.connect(true);
@@ -5929,6 +5959,8 @@
         waitForIdle();
 
         verify(mMockNetd, times(1)).networkCreatePhysical(eq(cellNetId), anyInt());
+        verify(mNetworkManagementService, times(1)).addRoute(eq(cellNetId), eq(defaultRoute));
+        verify(mNetworkManagementService, times(1)).addRoute(eq(cellNetId), eq(hostRoute));
         verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
         verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
                 TYPE_MOBILE);
@@ -5954,12 +5986,14 @@
 
         verifyNoMoreInteractions(mMockNetd);
         verifyNoMoreInteractions(mMockDnsResolver);
+        reset(mNetworkManagementService);
         reset(mMockNetd);
         reset(mMockDnsResolver);
+        when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
+                .thenReturn(getClatInterfaceConfig(myIpv4));
 
         // Remove IPv4 address. Expect prefix discovery to be started again.
         cellLp.removeLinkAddress(myIpv4);
-        cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
         mCellNetworkAgent.sendLinkProperties(cellLp);
         networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
@@ -5981,6 +6015,7 @@
         List<LinkProperties> stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork())
                 .getStackedLinks();
         assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0));
+        verify(mNetworkManagementService).addRoute(eq(cellNetId), eq(stackedDefault));
 
         // Change trivial linkproperties and see if stacked link is preserved.
         cellLp.addDnsServer(InetAddress.getByName("8.8.8.8"));
@@ -6006,8 +6041,9 @@
         // Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
         // linkproperties are cleaned up.
         cellLp.addLinkAddress(myIpv4);
-        cellLp.addRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
+        cellLp.addRoute(ipv4Default);
         mCellNetworkAgent.sendLinkProperties(cellLp);
+        verify(mNetworkManagementService).addRoute(eq(cellNetId), eq(stackedDefault));
         networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
         verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
         verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
@@ -6026,8 +6062,11 @@
 
         verifyNoMoreInteractions(mMockNetd);
         verifyNoMoreInteractions(mMockDnsResolver);
+        reset(mNetworkManagementService);
         reset(mMockNetd);
         reset(mMockDnsResolver);
+        when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
+                .thenReturn(getClatInterfaceConfig(myIpv4));
 
         // Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
         mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
@@ -6041,6 +6080,7 @@
         cellLp.removeDnsServer(InetAddress.getByName("8.8.8.8"));
         mCellNetworkAgent.sendLinkProperties(cellLp);
         networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+        verify(mNetworkManagementService, times(1)).removeRoute(eq(cellNetId), eq(ipv4Default));
         verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
         mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
                 kNat64PrefixString, 96);
@@ -6416,17 +6456,173 @@
         assertEquals(wifiLp, mService.getActiveLinkProperties());
     }
 
+    private void setupLocationPermissions(
+            int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.targetSdkVersion = targetSdk;
+        when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
+                .thenReturn(applicationInfo);
+
+        when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
+
+        if (op != null) {
+            when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
+                .thenReturn(AppOpsManager.MODE_ALLOWED);
+        }
+
+        if (perm != null) {
+            mServiceContext.setPermission(perm, PERMISSION_GRANTED);
+        }
+    }
+
+    private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) {
+        final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
+
+        return mService
+                .maybeSanitizeLocationInfoForCaller(netCap, callerUid, mContext.getPackageName())
+                .getOwnerUid();
+    }
+
     @Test
-    public void testNetworkCapabilitiesRestrictedForCallerPermissions() {
-        int callerUid = Process.myUid();
-        final NetworkCapabilities originalNc = new NetworkCapabilities();
-        originalNc.setOwnerUid(callerUid);
+    public void testMaybeSanitizeLocationInfoForCallerWithFineLocationAfterQ() throws Exception {
+        setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
 
-        final NetworkCapabilities newNc =
-                mService.networkCapabilitiesRestrictedForCallerPermissions(
-                        originalNc, Process.myPid(), callerUid);
+        final int myUid = Process.myUid();
+        assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+    }
 
-        assertEquals(Process.INVALID_UID, newNc.getOwnerUid());
+    @Test
+    public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationPreQ() throws Exception {
+        setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
+                Manifest.permission.ACCESS_COARSE_LOCATION);
+
+        final int myUid = Process.myUid();
+        assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+    }
+
+    @Test
+    public void testMaybeSanitizeLocationInfoForCallerLocationOff() throws Exception {
+        // Test that even with fine location permission, and UIDs matching, the UID is sanitized.
+        setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+
+        final int myUid = Process.myUid();
+        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+    }
+
+    @Test
+    public void testMaybeSanitizeLocationInfoForCallerWrongUid() throws Exception {
+        // Test that even with fine location permission, not being the owner leads to sanitization.
+        setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+
+        final int myUid = Process.myUid();
+        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid));
+    }
+
+    @Test
+    public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationAfterQ() throws Exception {
+        // Test that not having fine location permission leads to sanitization.
+        setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
+                Manifest.permission.ACCESS_COARSE_LOCATION);
+
+        // Test that without the location permission, the owner field is sanitized.
+        final int myUid = Process.myUid();
+        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+    }
+
+    @Test
+    public void testMaybeSanitizeLocationInfoForCallerWithoutLocationPermission() throws Exception {
+        setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
+
+        // Test that without the location permission, the owner field is sanitized.
+        final int myUid = Process.myUid();
+        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+    }
+
+    private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
+            throws Exception {
+        final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
+        establishVpn(new LinkProperties(), vpnOwnerUid, vpnRange);
+        mMockVpn.setVpnType(vpnType);
+
+        final VpnInfo vpnInfo = new VpnInfo();
+        vpnInfo.ownerUid = vpnOwnerUid;
+        mMockVpn.setVpnInfo(vpnInfo);
+    }
+
+    private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
+            throws Exception {
+        setupConnectionOwnerUid(vpnOwnerUid, vpnType);
+
+        // Test as VPN app
+        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+        mServiceContext.setPermission(
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_DENIED);
+    }
+
+    private ConnectionInfo getTestConnectionInfo() throws Exception {
+        return new ConnectionInfo(
+                IPPROTO_TCP,
+                new InetSocketAddress(InetAddresses.parseNumericAddress("1.2.3.4"), 1234),
+                new InetSocketAddress(InetAddresses.parseNumericAddress("2.3.4.5"), 2345));
+    }
+
+    @Test
+    public void testGetConnectionOwnerUidPlatformVpn() throws Exception {
+        final int myUid = Process.myUid();
+        setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_PLATFORM);
+
+        try {
+            mService.getConnectionOwnerUid(getTestConnectionInfo());
+            fail("Expected SecurityException for non-VpnService app");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    @Test
+    public void testGetConnectionOwnerUidVpnServiceWrongUser() throws Exception {
+        final int myUid = Process.myUid();
+        setupConnectionOwnerUidAsVpnApp(myUid + 1, VpnManager.TYPE_VPN_SERVICE);
+
+        try {
+            mService.getConnectionOwnerUid(getTestConnectionInfo());
+            fail("Expected SecurityException for non-VpnService app");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    @Test
+    public void testGetConnectionOwnerUidVpnServiceDoesNotThrow() throws Exception {
+        final int myUid = Process.myUid();
+        setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_SERVICE);
+
+        // TODO: Test the returned UID
+        mService.getConnectionOwnerUid(getTestConnectionInfo());
+    }
+
+    @Test
+    public void testGetConnectionOwnerUidVpnServiceNetworkStackDoesNotThrow() throws Exception {
+        final int myUid = Process.myUid();
+        setupConnectionOwnerUid(myUid, VpnManager.TYPE_VPN_SERVICE);
+        mServiceContext.setPermission(
+                android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+
+        // TODO: Test the returned UID
+        mService.getConnectionOwnerUid(getTestConnectionInfo());
+    }
+
+    @Test
+    public void testGetConnectionOwnerUidVpnServiceMainlineNetworkStackDoesNotThrow()
+            throws Exception {
+        final int myUid = Process.myUid();
+        setupConnectionOwnerUid(myUid, VpnManager.TYPE_VPN_SERVICE);
+        mServiceContext.setPermission(
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_GRANTED);
+
+        // TODO: Test the returned UID
+        mService.getConnectionOwnerUid(getTestConnectionInfo());
     }
 
     private TestNetworkAgentWrapper establishVpn(
@@ -6628,21 +6824,6 @@
                         mContext.getOpPackageName()));
     }
 
-    private void setupLocationPermissions(
-            int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
-        final ApplicationInfo applicationInfo = new ApplicationInfo();
-        applicationInfo.targetSdkVersion = targetSdk;
-        when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
-                .thenReturn(applicationInfo);
-
-        when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
-
-        when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
-                .thenReturn(AppOpsManager.MODE_ALLOWED);
-
-        mServiceContext.setPermission(perm, PERMISSION_GRANTED);
-    }
-
     private void setUpConnectivityDiagnosticsCallback() throws Exception {
         final NetworkRequest request = new NetworkRequest.Builder().build();
         when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index eb78529..1994d1f 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -59,9 +59,15 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
+import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.IpSecManager;
+import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo.DetailedState;
+import android.net.RouteInfo;
 import android.net.UidRange;
 import android.net.VpnManager;
 import android.net.VpnService;
@@ -72,6 +78,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.security.Credentials;
 import android.security.KeyStore;
 import android.util.ArrayMap;
@@ -83,6 +90,7 @@
 import com.android.internal.R;
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
+import com.android.server.IpSecService;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -92,6 +100,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.net.Inet4Address;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -124,6 +133,9 @@
     }
 
     static final String TEST_VPN_PKG = "com.dummy.vpn";
+    private static final String TEST_VPN_SERVER = "1.2.3.4";
+    private static final String TEST_VPN_IDENTITY = "identity";
+    private static final byte[] TEST_VPN_PSK = "psk".getBytes();
 
     /**
      * Names and UIDs for some fake packages. Important points:
@@ -150,23 +162,39 @@
     @Mock private Vpn.SystemServices mSystemServices;
     @Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator;
     @Mock private ConnectivityManager mConnectivityManager;
+    @Mock private IpSecService mIpSecService;
     @Mock private KeyStore mKeyStore;
-    private final VpnProfile mVpnProfile = new VpnProfile("key");
+    private final VpnProfile mVpnProfile;
+
+    private IpSecManager mIpSecManager;
+
+    public VpnTest() throws Exception {
+        // Build an actual VPN profile that is capable of being converted to and from an
+        // Ikev2VpnProfile
+        final Ikev2VpnProfile.Builder builder =
+                new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY);
+        builder.setAuthPsk(TEST_VPN_PSK);
+        mVpnProfile = builder.build().toVpnProfile();
+    }
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        mIpSecManager = new IpSecManager(mContext, mIpSecService);
+
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         setMockedPackages(mPackages);
 
-        when(mContext.getPackageName()).thenReturn(Vpn.class.getPackage().getName());
+        when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG);
+        when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG);
         when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
         when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
         when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
                 .thenReturn(mNotificationManager);
         when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE)))
                 .thenReturn(mConnectivityManager);
+        when(mContext.getSystemService(eq(Context.IPSEC_SERVICE))).thenReturn(mIpSecManager);
         when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
                 .thenReturn(Resources.getSystem().getString(
                         R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
@@ -260,17 +288,17 @@
         assertFalse(vpn.getLockdown());
 
         // Set always-on without lockdown.
-        assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList()));
+        assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList(), mKeyStore));
         assertTrue(vpn.getAlwaysOn());
         assertFalse(vpn.getLockdown());
 
         // Set always-on with lockdown.
-        assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList()));
+        assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList(), mKeyStore));
         assertTrue(vpn.getAlwaysOn());
         assertTrue(vpn.getLockdown());
 
         // Remove always-on configuration.
-        assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList()));
+        assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList(), mKeyStore));
         assertFalse(vpn.getAlwaysOn());
         assertFalse(vpn.getLockdown());
     }
@@ -284,11 +312,11 @@
         assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
 
         // Set always-on without lockdown.
-        assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null));
+        assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null, mKeyStore));
         assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
 
         // Set always-on with lockdown.
-        assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null));
+        assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null, mKeyStore));
         verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
             new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
             new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -297,7 +325,7 @@
         assertUnblocked(vpn, user.start + PKG_UIDS[1]);
 
         // Switch to another app.
-        assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
+        assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
         verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
             new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
             new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -316,7 +344,8 @@
         final UidRange user = UidRange.createForUser(primaryUser.id);
 
         // Set always-on with lockdown and whitelist app PKGS[2] from lockdown.
-        assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[2])));
+        assertTrue(vpn.setAlwaysOnPackage(
+                PKGS[1], true, Collections.singletonList(PKGS[2]), mKeyStore));
         verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
                 new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
                 new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
@@ -325,7 +354,8 @@
         assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
 
         // Change whitelisted app to PKGS[3].
-        assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[3])));
+        assertTrue(vpn.setAlwaysOnPackage(
+                PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore));
         verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
                 new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
         }));
@@ -337,7 +367,8 @@
         assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[3]);
 
         // Change the VPN app.
-        assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[3])));
+        assertTrue(vpn.setAlwaysOnPackage(
+                PKGS[0], true, Collections.singletonList(PKGS[3]), mKeyStore));
         verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
                 new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
                 new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
@@ -350,7 +381,7 @@
         assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
 
         // Remove the whitelist.
-        assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null));
+        assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore));
         verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
                 new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
                 new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
@@ -363,7 +394,8 @@
         assertUnblocked(vpn, user.start + PKG_UIDS[0]);
 
         // Add the whitelist.
-        assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[1])));
+        assertTrue(vpn.setAlwaysOnPackage(
+                PKGS[0], true, Collections.singletonList(PKGS[1]), mKeyStore));
         verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
                 new UidRange(user.start + PKG_UIDS[0] + 1, user.stop)
         }));
@@ -375,12 +407,13 @@
         assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]);
 
         // Try whitelisting a package with a comma, should be rejected.
-        assertFalse(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList("a.b,c.d")));
+        assertFalse(vpn.setAlwaysOnPackage(
+                PKGS[0], true, Collections.singletonList("a.b,c.d"), mKeyStore));
 
         // Pass a non-existent packages in the whitelist, they (and only they) should be ignored.
         // Whitelisted package should change from PGKS[1] to PKGS[2].
-        assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true,
-                Arrays.asList("com.foo.app", PKGS[2], "com.bar.app")));
+        assertTrue(vpn.setAlwaysOnPackage(
+                PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"), mKeyStore));
         verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[]{
                 new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
                 new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -405,7 +438,7 @@
         final UidRange profile = UidRange.createForUser(tempProfile.id);
 
         // Set lockdown.
-        assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
+        assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
         verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
             new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
             new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
@@ -499,22 +532,22 @@
                 .thenReturn(Collections.singletonList(resInfo));
 
         // null package name should return false
-        assertFalse(vpn.isAlwaysOnPackageSupported(null));
+        assertFalse(vpn.isAlwaysOnPackageSupported(null, mKeyStore));
 
         // Pre-N apps are not supported
         appInfo.targetSdkVersion = VERSION_CODES.M;
-        assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+        assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
 
         // N+ apps are supported by default
         appInfo.targetSdkVersion = VERSION_CODES.N;
-        assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+        assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
 
         // Apps that opt out explicitly are not supported
         appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
         Bundle metaData = new Bundle();
         metaData.putBoolean(VpnService.SERVICE_META_DATA_SUPPORTS_ALWAYS_ON, false);
         svcInfo.metaData = metaData;
-        assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+        assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
     }
 
     @Test
@@ -531,7 +564,7 @@
                 .cancelAsUser(anyString(), anyInt(), eq(userHandle));
 
         // Start showing a notification for disconnected once always-on.
-        vpn.setAlwaysOnPackage(PKGS[0], false, null);
+        vpn.setAlwaysOnPackage(PKGS[0], false, null, mKeyStore);
         order.verify(mNotificationManager)
                 .notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
 
@@ -545,7 +578,7 @@
                 .notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
 
         // Notification should be cleared after unsetting always-on package.
-        vpn.setAlwaysOnPackage(null, false, null);
+        vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
         order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle));
     }
 
@@ -656,8 +689,12 @@
     }
 
     private Vpn createVpnAndSetupUidChecks(int... grantedOps) throws Exception {
-        final Vpn vpn = createVpn(primaryUser.id);
-        setMockedUsers(primaryUser);
+        return createVpnAndSetupUidChecks(primaryUser, grantedOps);
+    }
+
+    private Vpn createVpnAndSetupUidChecks(UserInfo user, int... grantedOps) throws Exception {
+        final Vpn vpn = createVpn(user.id);
+        setMockedUsers(user);
 
         when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
                 .thenReturn(Process.myUid());
@@ -726,6 +763,19 @@
     }
 
     @Test
+    public void testProvisionVpnProfileRestrictedUser() throws Exception {
+        final Vpn vpn =
+                createVpnAndSetupUidChecks(
+                        restrictedProfileA, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+        try {
+            vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile, mKeyStore);
+            fail("Expected SecurityException due to restricted user");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    @Test
     public void testDeleteVpnProfile() throws Exception {
         final Vpn vpn = createVpnAndSetupUidChecks();
 
@@ -736,6 +786,19 @@
     }
 
     @Test
+    public void testDeleteVpnProfileRestrictedUser() throws Exception {
+        final Vpn vpn =
+                createVpnAndSetupUidChecks(
+                        restrictedProfileA, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+        try {
+            vpn.deleteVpnProfile(TEST_VPN_PKG, mKeyStore);
+            fail("Expected SecurityException due to restricted user");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    @Test
     public void testGetVpnProfilePrivileged() throws Exception {
         final Vpn vpn = createVpnAndSetupUidChecks();
 
@@ -820,6 +883,32 @@
     }
 
     @Test
+    public void testStartVpnProfileRestrictedUser() throws Exception {
+        final Vpn vpn =
+                createVpnAndSetupUidChecks(
+                        restrictedProfileA, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+        try {
+            vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+            fail("Expected SecurityException due to restricted user");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    @Test
+    public void testStopVpnProfileRestrictedUser() throws Exception {
+        final Vpn vpn =
+                createVpnAndSetupUidChecks(
+                        restrictedProfileA, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+        try {
+            vpn.stopVpnProfile(TEST_VPN_PKG);
+            fail("Expected SecurityException due to restricted user");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    @Test
     public void testSetPackageAuthorizationVpnService() throws Exception {
         final Vpn vpn = createVpnAndSetupUidChecks();
 
@@ -864,12 +953,68 @@
                         eq(AppOpsManager.MODE_IGNORED));
     }
 
+    private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) {
+        assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, lockdownEnabled, null, mKeyStore));
+
+        verify(mKeyStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+        verify(mAppOps).setMode(
+                eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN), eq(uid), eq(TEST_VPN_PKG),
+                eq(AppOpsManager.MODE_ALLOWED));
+
+        verify(mSystemServices).settingsSecurePutStringForUser(
+                eq(Settings.Secure.ALWAYS_ON_VPN_APP), eq(TEST_VPN_PKG), eq(primaryUser.id));
+        verify(mSystemServices).settingsSecurePutIntForUser(
+                eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN), eq(lockdownEnabled ? 1 : 0),
+                eq(primaryUser.id));
+        verify(mSystemServices).settingsSecurePutStringForUser(
+                eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST), eq(""), eq(primaryUser.id));
+    }
+
+    @Test
+    public void testSetAndStartAlwaysOnVpn() throws Exception {
+        final Vpn vpn = createVpn(primaryUser.id);
+        setMockedUsers(primaryUser);
+
+        // UID checks must return a different UID; otherwise it'll be treated as already prepared.
+        final int uid = Process.myUid() + 1;
+        when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
+                .thenReturn(uid);
+        when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+                .thenReturn(mVpnProfile.encode());
+
+        setAndVerifyAlwaysOnPackage(vpn, uid, false);
+        assertTrue(vpn.startAlwaysOnVpn(mKeyStore));
+
+        // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
+        // a subsequent CL.
+    }
+
+    @Test
+    public void testStartLegacyVpn() throws Exception {
+        final Vpn vpn = createVpn(primaryUser.id);
+        setMockedUsers(primaryUser);
+
+        // Dummy egress interface
+        final String egressIface = "DUMMY0";
+        final LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName(egressIface);
+
+        final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
+                        InetAddresses.parseNumericAddress("192.0.2.0"), egressIface);
+        lp.addRoute(defaultRoute);
+
+        vpn.startLegacyVpn(mVpnProfile, mKeyStore, lp);
+
+        // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
+        // a subsequent CL.
+    }
+
     /**
      * Mock some methods of vpn object.
      */
     private Vpn createVpn(@UserIdInt int userId) {
         return new Vpn(Looper.myLooper(), mContext, mNetService,
-                userId, mSystemServices, mIkev2SessionCreator);
+                userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
     }
 
     private static void assertBlocked(Vpn vpn, int... uids) {
diff --git a/tools/aapt/tests/AaptConfig_test.cpp b/tools/aapt/tests/AaptConfig_test.cpp
index 4f22fa5..b7c6bd2 100644
--- a/tools/aapt/tests/AaptConfig_test.cpp
+++ b/tools/aapt/tests/AaptConfig_test.cpp
@@ -20,7 +20,6 @@
 #include "AaptConfig.h"
 #include "ConfigDescription.h"
 #include "SdkConstants.h"
-#include "TestHelper.h"
 
 using android::String8;
 
@@ -127,4 +126,4 @@
               config.colorMode & android::ResTable_config::MASK_HDR);
     EXPECT_EQ(SDK_O, config.sdkVersion);
     EXPECT_EQ(String8("lowdr-v26"), config.toString());
-}
\ No newline at end of file
+}
diff --git a/tools/aapt/tests/AaptGroupEntry_test.cpp b/tools/aapt/tests/AaptGroupEntry_test.cpp
index 7348a08..bf5ca59 100644
--- a/tools/aapt/tests/AaptGroupEntry_test.cpp
+++ b/tools/aapt/tests/AaptGroupEntry_test.cpp
@@ -19,7 +19,6 @@
 
 #include "AaptAssets.h"
 #include "ResourceFilter.h"
-#include "TestHelper.h"
 
 using android::String8;
 
diff --git a/tools/aapt/tests/ResourceTable_test.cpp b/tools/aapt/tests/ResourceTable_test.cpp
index f2c696b..0d550df 100644
--- a/tools/aapt/tests/ResourceTable_test.cpp
+++ b/tools/aapt/tests/ResourceTable_test.cpp
@@ -19,7 +19,6 @@
 
 #include "ConfigDescription.h"
 #include "ResourceTable.h"
-#include "TestHelper.h"
 
 using android::String16;
 
diff --git a/tools/aapt/tests/TestHelper.h b/tools/aapt/tests/TestHelper.h
deleted file mode 100644
index 79174832..0000000
--- a/tools/aapt/tests/TestHelper.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __TEST_HELPER_H
-#define __TEST_HELPER_H
-
-#include <utils/String8.h>
-
-namespace android {
-
-/**
- * Stream operator for nicely printing String8's in gtest output.
- */
-inline std::ostream& operator<<(std::ostream& stream, const String8& str) {
-    return stream << str.string();
-}
-
-}
-
-#endif
diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py
index 9661927..6a5b0e1 100755
--- a/tools/hiddenapi/merge_csv.py
+++ b/tools/hiddenapi/merge_csv.py
@@ -14,26 +14,56 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 """
-Merge mutliple CSV files, possibly with different columns, writing to stdout.
+Merge multiple CSV files, possibly with different columns.
 """
 
+import argparse
 import csv
-import sys
+import io
 
-csv_readers = [
-    csv.DictReader(open(csv_file, 'r'), delimiter=',', quotechar='|')
-    for csv_file in sys.argv[1:]
-]
+from zipfile import ZipFile
 
-# Build union of all columns from source files:
+args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.')
+args_parser.add_argument('--header', help='Comma separated field names; '
+                                          'if missing determines the header from input files.')
+args_parser.add_argument('--zip_input', help='ZIP archive with all CSV files to merge.')
+args_parser.add_argument('--output', help='Output file for merged CSV.',
+                         default='-', type=argparse.FileType('w'))
+args_parser.add_argument('files', nargs=argparse.REMAINDER)
+args = args_parser.parse_args()
+
+
+def dict_reader(input):
+    return csv.DictReader(input, delimiter=',', quotechar='|')
+
+
+if args.zip_input and len(args.files) > 0:
+    raise ValueError('Expecting either a single ZIP with CSV files'
+                     ' or a list of CSV files as input; not both.')
+
+csv_readers = []
+if len(args.files) > 0:
+    for file in args.files:
+        csv_readers.append(dict_reader(open(file, 'r')))
+elif args.zip_input:
+    with ZipFile(args.zip_input) as zip:
+        for entry in zip.namelist():
+            if entry.endswith('.uau'):
+                csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
+
 headers = set()
-for reader in csv_readers:
-    headers = headers.union(reader.fieldnames)
+if args.header:
+    fieldnames = args.header.split(',')
+else:
+    # Build union of all columns from source files:
+    for reader in csv_readers:
+        headers = headers.union(reader.fieldnames)
+    fieldnames = sorted(headers)
 
 # Concatenate all files to output:
-out = csv.DictWriter(sys.stdout, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
-                     dialect='unix', fieldnames=sorted(headers))
-out.writeheader()
+writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
+                        dialect='unix', fieldnames=fieldnames)
+writer.writeheader()
 for reader in csv_readers:
     for row in reader:
-        out.writerow(row)
+        writer.writerow(row)